blob: d525bdcfec7e0901532d8f485325432d4ad5e630 [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"
Cole Fausta03ac3a2024-01-12 12:12:26 -080023
24 "github.com/google/blueprint/parser"
25 "github.com/google/blueprint/proptools"
Colin Cross9d34f352019-11-22 16:03:51 -080026)
27
Liz Kammer432bd592020-12-16 12:42:02 -080028const conditionsDefault = "conditions_default"
29
Jingwen Chena47f28d2021-11-02 16:43:57 +000030var SoongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
Colin Cross9d34f352019-11-22 16:03:51 -080031
32// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
33// result so each file is only parsed once.
34func Parse(r io.Reader, from string) (*SoongConfigDefinition, []error) {
35 scope := parser.NewScope(nil)
36 file, errs := parser.ParseAndEval(from, r, scope)
37
38 if len(errs) > 0 {
39 return nil, errs
40 }
41
42 mtDef := &SoongConfigDefinition{
43 ModuleTypes: make(map[string]*ModuleType),
44 variables: make(map[string]soongConfigVariable),
45 }
46
47 for _, def := range file.Defs {
48 switch def := def.(type) {
49 case *parser.Module:
50 newErrs := processImportModuleDef(mtDef, def)
51
52 if len(newErrs) > 0 {
53 errs = append(errs, newErrs...)
54 }
55
56 case *parser.Assignment:
57 // Already handled via Scope object
58 default:
59 panic("unknown definition type")
60 }
61 }
62
63 if len(errs) > 0 {
64 return nil, errs
65 }
66
67 for name, moduleType := range mtDef.ModuleTypes {
68 for _, varName := range moduleType.variableNames {
69 if v, ok := mtDef.variables[varName]; ok {
70 moduleType.Variables = append(moduleType.Variables, v)
71 } else {
72 return nil, []error{
73 fmt.Errorf("unknown variable %q in module type %q", varName, name),
74 }
75 }
76 }
77 }
78
79 return mtDef, nil
80}
81
82func processImportModuleDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
83 switch def.Type {
84 case "soong_config_module_type":
85 return processModuleTypeDef(v, def)
86 case "soong_config_string_variable":
87 return processStringVariableDef(v, def)
88 case "soong_config_bool_variable":
89 return processBoolVariableDef(v, def)
90 default:
91 // Unknown module types will be handled when the file is parsed as a normal
92 // Android.bp file.
93 }
94
95 return nil
96}
97
98type ModuleTypeProperties struct {
99 // the name of the new module type. Unlike most modules, this name does not need to be unique,
100 // although only one module type with any name will be importable into an Android.bp file.
101 Name string
102
103 // the module type that this module type will extend.
104 Module_type string
105
106 // the SOONG_CONFIG_NAMESPACE value from a BoardConfig.mk that this module type will read
107 // configuration variables from.
108 Config_namespace string
109
110 // the list of SOONG_CONFIG variables that this module type will read
111 Variables []string
112
Dan Willemsen2b8b89c2020-03-23 19:39:34 -0700113 // the list of boolean SOONG_CONFIG variables that this module type will read
114 Bool_variables []string
115
Dan Willemsenb0935db2020-03-23 19:42:18 -0700116 // the list of SOONG_CONFIG variables that this module type will read. The value will be
117 // inserted into the properties with %s substitution.
118 Value_variables []string
119
Colin Cross9d34f352019-11-22 16:03:51 -0800120 // the list of properties that this module type will extend.
121 Properties []string
122}
123
124func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
125
126 props := &ModuleTypeProperties{}
127
128 _, errs = proptools.UnpackProperties(def.Properties, props)
129 if len(errs) > 0 {
130 return errs
131 }
132
133 if props.Name == "" {
134 errs = append(errs, fmt.Errorf("name property must be set"))
135 }
136
137 if props.Config_namespace == "" {
138 errs = append(errs, fmt.Errorf("config_namespace property must be set"))
139 }
140
141 if props.Module_type == "" {
142 errs = append(errs, fmt.Errorf("module_type property must be set"))
143 }
144
145 if len(errs) > 0 {
146 return errs
147 }
148
Liz Kammer432bd592020-12-16 12:42:02 -0800149 if mt, errs := newModuleType(props); len(errs) > 0 {
150 return errs
151 } else {
152 v.ModuleTypes[props.Name] = mt
Dan Willemsenb0935db2020-03-23 19:42:18 -0700153 }
154
Colin Cross9d34f352019-11-22 16:03:51 -0800155 return nil
156}
157
158type VariableProperties struct {
159 Name string
160}
161
162type StringVariableProperties struct {
163 Values []string
164}
165
166func processStringVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
167 stringProps := &StringVariableProperties{}
168
169 base, errs := processVariableDef(def, stringProps)
170 if len(errs) > 0 {
171 return errs
172 }
173
174 if len(stringProps.Values) == 0 {
175 return []error{fmt.Errorf("values property must be set")}
176 }
177
Liz Kammer72beb342022-02-03 08:42:10 -0500178 vals := make(map[string]bool, len(stringProps.Values))
Liz Kammer432bd592020-12-16 12:42:02 -0800179 for _, name := range stringProps.Values {
180 if err := checkVariableName(name); err != nil {
181 return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
Liz Kammer72beb342022-02-03 08:42:10 -0500182 } else if _, ok := vals[name]; ok {
183 return []error{fmt.Errorf("soong_config_string_variable: values property error: duplicate value: %q", name)}
Liz Kammer432bd592020-12-16 12:42:02 -0800184 }
Liz Kammer72beb342022-02-03 08:42:10 -0500185 vals[name] = true
Liz Kammer432bd592020-12-16 12:42:02 -0800186 }
187
Colin Cross9d34f352019-11-22 16:03:51 -0800188 v.variables[base.variable] = &stringVariable{
189 baseVariable: base,
190 values: CanonicalizeToProperties(stringProps.Values),
191 }
192
193 return nil
194}
195
196func processBoolVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
197 base, errs := processVariableDef(def)
198 if len(errs) > 0 {
199 return errs
200 }
201
202 v.variables[base.variable] = &boolVariable{
203 baseVariable: base,
204 }
205
206 return nil
207}
208
209func processVariableDef(def *parser.Module,
210 extraProps ...interface{}) (cond baseVariable, errs []error) {
211
212 props := &VariableProperties{}
213
214 allProps := append([]interface{}{props}, extraProps...)
215
216 _, errs = proptools.UnpackProperties(def.Properties, allProps...)
217 if len(errs) > 0 {
218 return baseVariable{}, errs
219 }
220
221 if props.Name == "" {
222 return baseVariable{}, []error{fmt.Errorf("name property must be set")}
223 }
224
225 return baseVariable{
226 variable: props.Name,
227 }, nil
228}
229
230type SoongConfigDefinition struct {
231 ModuleTypes map[string]*ModuleType
232
233 variables map[string]soongConfigVariable
234}
235
236// CreateProperties returns a reflect.Value of a newly constructed type that contains the desired
237// property layout for the Soong config variables, with each possible value an interface{} that
238// contains a nil pointer to another newly constructed type that contains the affectable properties.
239// The reflect.Value will be cloned for each call to the Soong config module type's factory method.
240//
241// For example, the acme_cc_defaults example above would
242// produce a reflect.Value whose type is:
Colin Crossd079e0b2022-08-16 10:27:33 -0700243//
244// *struct {
245// Soong_config_variables struct {
246// Board struct {
247// Soc_a interface{}
248// Soc_b interface{}
249// }
250// }
251// }
252//
Colin Cross9d34f352019-11-22 16:03:51 -0800253// And whose value is:
Colin Crossd079e0b2022-08-16 10:27:33 -0700254//
255// &{
256// Soong_config_variables: {
257// Board: {
258// Soc_a: (*struct{ Cflags []string })(nil),
259// Soc_b: (*struct{ Cflags []string })(nil),
260// },
261// },
262// }
Paul Duffine8b47682023-01-09 15:42:57 +0000263func CreateProperties(factoryProps []interface{}, moduleType *ModuleType) reflect.Value {
Colin Cross9d34f352019-11-22 16:03:51 -0800264 var fields []reflect.StructField
265
Colin Cross9d34f352019-11-22 16:03:51 -0800266 affectablePropertiesType := createAffectablePropertiesType(moduleType.affectableProperties, factoryProps)
267 if affectablePropertiesType == nil {
268 return reflect.Value{}
269 }
270
271 for _, c := range moduleType.Variables {
272 fields = append(fields, reflect.StructField{
273 Name: proptools.FieldNameForProperty(c.variableProperty()),
274 Type: c.variableValuesType(),
275 })
276 }
277
278 typ := reflect.StructOf([]reflect.StructField{{
Jingwen Chena47f28d2021-11-02 16:43:57 +0000279 Name: SoongConfigProperty,
Colin Cross9d34f352019-11-22 16:03:51 -0800280 Type: reflect.StructOf(fields),
281 }})
282
283 props := reflect.New(typ)
Jingwen Chena47f28d2021-11-02 16:43:57 +0000284 structConditions := props.Elem().FieldByName(SoongConfigProperty)
Colin Cross9d34f352019-11-22 16:03:51 -0800285
286 for i, c := range moduleType.Variables {
287 c.initializeProperties(structConditions.Field(i), affectablePropertiesType)
288 }
289
290 return props
291}
292
293// createAffectablePropertiesType creates a reflect.Type of a struct that has a field for each affectable property
294// that exists in factoryProps.
295func createAffectablePropertiesType(affectableProperties []string, factoryProps []interface{}) reflect.Type {
296 affectableProperties = append([]string(nil), affectableProperties...)
297 sort.Strings(affectableProperties)
298
299 var recurse func(prefix string, aps []string) ([]string, reflect.Type)
300 recurse = func(prefix string, aps []string) ([]string, reflect.Type) {
301 var fields []reflect.StructField
302
Colin Cross997f27a2021-03-05 17:25:41 -0800303 // Iterate while the list is non-empty so it can be modified in the loop.
Colin Cross9d34f352019-11-22 16:03:51 -0800304 for len(affectableProperties) > 0 {
305 p := affectableProperties[0]
306 if !strings.HasPrefix(affectableProperties[0], prefix) {
Colin Cross997f27a2021-03-05 17:25:41 -0800307 // The properties are sorted and recurse is always called with a prefix that matches
308 // the first property in the list, so if we've reached one that doesn't match the
309 // prefix we are done with this prefix.
Colin Cross9d34f352019-11-22 16:03:51 -0800310 break
311 }
Colin Cross9d34f352019-11-22 16:03:51 -0800312
313 nestedProperty := strings.TrimPrefix(p, prefix)
314 if i := strings.IndexRune(nestedProperty, '.'); i >= 0 {
315 var nestedType reflect.Type
316 nestedPrefix := nestedProperty[:i+1]
317
Colin Cross997f27a2021-03-05 17:25:41 -0800318 // Recurse to handle the properties with the found prefix. This will return
319 // an updated affectableProperties with the handled entries removed from the front
320 // of the list, and the type that contains the handled entries. The type may be
321 // nil if none of the entries matched factoryProps.
Colin Cross9d34f352019-11-22 16:03:51 -0800322 affectableProperties, nestedType = recurse(prefix+nestedPrefix, affectableProperties)
323
324 if nestedType != nil {
325 nestedFieldName := proptools.FieldNameForProperty(strings.TrimSuffix(nestedPrefix, "."))
326
327 fields = append(fields, reflect.StructField{
328 Name: nestedFieldName,
329 Type: nestedType,
330 })
331 }
332 } else {
333 typ := typeForPropertyFromPropertyStructs(factoryProps, p)
334 if typ != nil {
335 fields = append(fields, reflect.StructField{
336 Name: proptools.FieldNameForProperty(nestedProperty),
337 Type: typ,
338 })
339 }
Colin Cross997f27a2021-03-05 17:25:41 -0800340 // The first element in the list has been handled, remove it from the list.
341 affectableProperties = affectableProperties[1:]
Colin Cross9d34f352019-11-22 16:03:51 -0800342 }
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
Liz Kammer432bd592020-12-16 12:42:02 -0800420// soong_config_variables are expected to be in the same order as moduleType.Variables.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700421func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
Colin Cross9d34f352019-11-22 16:03:51 -0800422 var ret []interface{}
Jingwen Chena47f28d2021-11-02 16:43:57 +0000423 props = props.Elem().FieldByName(SoongConfigProperty)
Colin Cross9d34f352019-11-22 16:03:51 -0800424 for i, c := range moduleType.Variables {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700425 if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
426 return nil, err
427 } else if ps != nil {
Colin Cross9d34f352019-11-22 16:03:51 -0800428 ret = append(ret, ps)
429 }
430 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700431 return ret, nil
Colin Cross9d34f352019-11-22 16:03:51 -0800432}
433
434type ModuleType struct {
435 BaseModuleType string
436 ConfigNamespace string
437 Variables []soongConfigVariable
438
439 affectableProperties []string
440 variableNames []string
441}
442
Liz Kammer432bd592020-12-16 12:42:02 -0800443func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
444 mt := &ModuleType{
445 affectableProperties: props.Properties,
446 ConfigNamespace: props.Config_namespace,
447 BaseModuleType: props.Module_type,
448 variableNames: props.Variables,
449 }
450
451 for _, name := range props.Bool_variables {
452 if err := checkVariableName(name); err != nil {
453 return nil, []error{fmt.Errorf("bool_variables %s", err)}
454 }
455
456 mt.Variables = append(mt.Variables, newBoolVariable(name))
457 }
458
459 for _, name := range props.Value_variables {
460 if err := checkVariableName(name); err != nil {
461 return nil, []error{fmt.Errorf("value_variables %s", err)}
462 }
463
464 mt.Variables = append(mt.Variables, &valueVariable{
465 baseVariable: baseVariable{
466 variable: name,
467 },
468 })
469 }
470
471 return mt, nil
472}
473
474func checkVariableName(name string) error {
475 if name == "" {
476 return fmt.Errorf("name must not be blank")
477 } else if name == conditionsDefault {
478 return fmt.Errorf("%q is reserved", conditionsDefault)
479 }
480 return nil
481}
482
Colin Cross9d34f352019-11-22 16:03:51 -0800483type soongConfigVariable interface {
484 // variableProperty returns the name of the variable.
485 variableProperty() string
486
487 // conditionalValuesType returns a reflect.Type that contains an interface{} for each possible value.
488 variableValuesType() reflect.Type
489
490 // initializeProperties is passed a reflect.Value of the reflect.Type returned by conditionalValuesType and a
491 // reflect.Type of the affectable properties, and should initialize each interface{} in the reflect.Value with
492 // the zero value of the affectable properties type.
493 initializeProperties(v reflect.Value, typ reflect.Type)
494
495 // PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
496 // to the module.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700497 PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
Colin Cross9d34f352019-11-22 16:03:51 -0800498}
499
500type baseVariable struct {
501 variable string
502}
503
504func (c *baseVariable) variableProperty() string {
505 return CanonicalizeToProperty(c.variable)
506}
507
508type stringVariable struct {
509 baseVariable
510 values []string
511}
512
513func (s *stringVariable) variableValuesType() reflect.Type {
514 var fields []reflect.StructField
515
Liz Kammer432bd592020-12-16 12:42:02 -0800516 var values []string
517 values = append(values, s.values...)
518 values = append(values, conditionsDefault)
519 for _, v := range values {
Colin Cross9d34f352019-11-22 16:03:51 -0800520 fields = append(fields, reflect.StructField{
521 Name: proptools.FieldNameForProperty(v),
522 Type: emptyInterfaceType,
523 })
524 }
525
526 return reflect.StructOf(fields)
527}
528
Liz Kammer432bd592020-12-16 12:42:02 -0800529// initializeProperties initializes properties to zero value of typ for supported values and a final
530// conditions default field.
Colin Cross9d34f352019-11-22 16:03:51 -0800531func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
532 for i := range s.values {
533 v.Field(i).Set(reflect.Zero(typ))
534 }
Liz Kammer432bd592020-12-16 12:42:02 -0800535 v.Field(len(s.values)).Set(reflect.Zero(typ)) // conditions default is the final value
Colin Cross9d34f352019-11-22 16:03:51 -0800536}
537
Liz Kammer432bd592020-12-16 12:42:02 -0800538// Extracts an interface from values containing the properties to apply based on config.
539// 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 -0700540func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Cole Faustb0a91332022-11-01 17:10:23 +0000541 configValue := config.String(s.variable)
542 if configValue != "" && !InList(configValue, s.values) {
543 return nil, fmt.Errorf("Soong config property %q must be one of %v, found %q", s.variable, s.values, configValue)
544 }
Colin Cross9d34f352019-11-22 16:03:51 -0800545 for j, v := range s.values {
Liz Kammer432bd592020-12-16 12:42:02 -0800546 f := values.Field(j)
Cole Faustb0a91332022-11-01 17:10:23 +0000547 if configValue == v && !f.Elem().IsNil() {
Liz Kammer432bd592020-12-16 12:42:02 -0800548 return f.Interface(), nil
Colin Cross9d34f352019-11-22 16:03:51 -0800549 }
550 }
Liz Kammer432bd592020-12-16 12:42:02 -0800551 // if we have reached this point, we have checked all valid values of string and either:
552 // * the value was not set
553 // * the value was set but that value was not specified in the Android.bp file
554 return values.Field(len(s.values)).Interface(), nil
Colin Cross9d34f352019-11-22 16:03:51 -0800555}
556
Liz Kammer432bd592020-12-16 12:42:02 -0800557// Struct to allow conditions set based on a boolean variable
Colin Cross9d34f352019-11-22 16:03:51 -0800558type boolVariable struct {
559 baseVariable
560}
561
Liz Kammer432bd592020-12-16 12:42:02 -0800562// newBoolVariable constructs a boolVariable with the given name
Liz Kammerfe8853d2020-12-16 09:34:33 -0800563func newBoolVariable(name string) *boolVariable {
564 return &boolVariable{
565 baseVariable{
566 variable: name,
567 },
568 }
569}
570
Colin Cross9d34f352019-11-22 16:03:51 -0800571func (b boolVariable) variableValuesType() reflect.Type {
572 return emptyInterfaceType
573}
574
Liz Kammer432bd592020-12-16 12:42:02 -0800575// initializeProperties initializes a property to zero value of typ with an additional conditions
576// default field.
Colin Cross9d34f352019-11-22 16:03:51 -0800577func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
Liz Kammer432bd592020-12-16 12:42:02 -0800578 initializePropertiesWithDefault(v, typ)
Colin Cross9d34f352019-11-22 16:03:51 -0800579}
580
Liz Kammer432bd592020-12-16 12:42:02 -0800581// initializePropertiesWithDefault, initialize with zero value, v to contain a field for each field
582// in typ, with an additional field for defaults of type typ. This should be used to initialize
583// boolVariable, valueVariable, or any future implementations of soongConfigVariable which support
584// one variable and a default.
585func initializePropertiesWithDefault(v reflect.Value, typ reflect.Type) {
586 sTyp := typ.Elem()
587 var fields []reflect.StructField
588 for i := 0; i < sTyp.NumField(); i++ {
589 fields = append(fields, sTyp.Field(i))
Colin Cross9d34f352019-11-22 16:03:51 -0800590 }
591
Liz Kammer432bd592020-12-16 12:42:02 -0800592 // create conditions_default field
593 nestedFieldName := proptools.FieldNameForProperty(conditionsDefault)
594 fields = append(fields, reflect.StructField{
595 Name: nestedFieldName,
596 Type: typ,
597 })
598
599 newTyp := reflect.PtrTo(reflect.StructOf(fields))
600 v.Set(reflect.Zero(newTyp))
601}
602
603// conditionsDefaultField extracts the conditions_default field from v. This is always the final
604// field if initialized with initializePropertiesWithDefault.
605func conditionsDefaultField(v reflect.Value) reflect.Value {
606 return v.Field(v.NumField() - 1)
607}
608
609// removeDefault removes the conditions_default field from values while retaining values from all
610// other fields. This allows
611func removeDefault(values reflect.Value) reflect.Value {
612 v := values.Elem().Elem()
613 s := conditionsDefaultField(v)
614 // if conditions_default field was not set, there will be no issues extending properties.
615 if !s.IsValid() {
616 return v
617 }
618
619 // If conditions_default field was set, it has the correct type for our property. Create a new
620 // reflect.Value of the conditions_default type and copy all fields (except for
621 // conditions_default) based on values to the result.
622 res := reflect.New(s.Type().Elem())
623 for i := 0; i < res.Type().Elem().NumField(); i++ {
624 val := v.Field(i)
625 res.Elem().Field(i).Set(val)
626 }
627
628 return res
629}
630
631// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
632// the module. If the value was not set, conditions_default interface will be returned; otherwise,
633// the interface in values, without conditions_default will be returned.
634func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
635 // If this variable was not referenced in the module, there are no properties to apply.
636 if values.Elem().IsZero() {
637 return nil, nil
638 }
639 if config.Bool(b.variable) {
640 values = removeDefault(values)
641 return values.Interface(), nil
642 }
643 v := values.Elem().Elem()
644 if f := conditionsDefaultField(v); f.IsValid() {
645 return f.Interface(), nil
646 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700647 return nil, nil
648}
649
Liz Kammer432bd592020-12-16 12:42:02 -0800650// Struct to allow conditions set based on a value variable, supporting string substitution.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700651type valueVariable struct {
652 baseVariable
653}
654
655func (s *valueVariable) variableValuesType() reflect.Type {
656 return emptyInterfaceType
657}
658
Liz Kammer432bd592020-12-16 12:42:02 -0800659// initializeProperties initializes a property to zero value of typ with an additional conditions
660// default field.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700661func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
Liz Kammer432bd592020-12-16 12:42:02 -0800662 initializePropertiesWithDefault(v, typ)
Dan Willemsenb0935db2020-03-23 19:42:18 -0700663}
664
Liz Kammer432bd592020-12-16 12:42:02 -0800665// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
666// the module. If the variable was not set, conditions_default interface will be returned;
667// otherwise, the interface in values, without conditions_default will be returned with all
668// appropriate string substitutions based on variable being set.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700669func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Liz Kammer432bd592020-12-16 12:42:02 -0800670 // If this variable was not referenced in the module, there are no properties to apply.
671 if !values.IsValid() || values.Elem().IsZero() {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700672 return nil, nil
673 }
Liz Kammer432bd592020-12-16 12:42:02 -0800674 if !config.IsSet(s.variable) {
675 return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
676 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700677 configValue := config.String(s.variable)
678
Liz Kammer432bd592020-12-16 12:42:02 -0800679 values = removeDefault(values)
680 propStruct := values.Elem()
Liz Kammer40ddfaa2020-12-16 10:59:00 -0800681 if !propStruct.IsValid() {
682 return nil, nil
683 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700684 for i := 0; i < propStruct.NumField(); i++ {
685 field := propStruct.Field(i)
686 kind := field.Kind()
687 if kind == reflect.Ptr {
688 if field.IsNil() {
689 continue
690 }
691 field = field.Elem()
Cole Fausta03ac3a2024-01-12 12:12:26 -0800692 kind = field.Kind()
Dan Willemsenb0935db2020-03-23 19:42:18 -0700693 }
694 switch kind {
695 case reflect.String:
696 err := printfIntoProperty(field, configValue)
697 if err != nil {
698 return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
699 }
700 case reflect.Slice:
701 for j := 0; j < field.Len(); j++ {
702 err := printfIntoProperty(field.Index(j), configValue)
703 if err != nil {
704 return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
705 }
706 }
707 case reflect.Bool:
708 // Nothing to do
709 default:
710 return nil, fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, propStruct.Type().Field(i).Name, kind)
711 }
712 }
713
714 return values.Interface(), nil
715}
716
717func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
718 s := propertyValue.String()
719
720 count := strings.Count(s, "%")
721 if count == 0 {
722 return nil
723 }
724
725 if count > 1 {
726 return fmt.Errorf("value variable properties only support a single '%%'")
727 }
728
729 if !strings.Contains(s, "%s") {
730 return fmt.Errorf("unsupported %% in value variable property")
731 }
732
733 propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, configValue)))
734
Colin Cross9d34f352019-11-22 16:03:51 -0800735 return nil
736}
737
738func CanonicalizeToProperty(v string) string {
739 return strings.Map(func(r rune) rune {
740 switch {
741 case r >= 'A' && r <= 'Z',
742 r >= 'a' && r <= 'z',
743 r >= '0' && r <= '9',
744 r == '_':
745 return r
746 default:
747 return '_'
748 }
749 }, v)
750}
751
752func CanonicalizeToProperties(values []string) []string {
753 ret := make([]string, len(values))
754 for i, v := range values {
755 ret[i] = CanonicalizeToProperty(v)
756 }
757 return ret
758}
759
760type emptyInterfaceStruct struct {
761 i interface{}
762}
763
764var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type
Cole Faustb0a91332022-11-01 17:10:23 +0000765
766// InList checks if the string belongs to the list
767func InList(s string, list []string) bool {
768 for _, s2 := range list {
769 if s2 == s {
770 return true
771 }
772 }
773 return false
774}