blob: f6046d00c7be90a09bb3e41c1ce52ad90938daa9 [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
Inseob Kim02c86182024-04-05 17:52:06 +0900120 // the list of SOONG_CONFIG list variables that this module type will read. Each value will be
121 // inserted into the properties with %s substitution.
122 List_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
240// CreateProperties returns a reflect.Value of a newly constructed type that contains the desired
241// property layout for the Soong config variables, with each possible value an interface{} that
242// contains a nil pointer to another newly constructed type that contains the affectable properties.
243// The reflect.Value will be cloned for each call to the Soong config module type's factory method.
244//
245// For example, the acme_cc_defaults example above would
246// produce a reflect.Value whose type is:
Colin Crossd079e0b2022-08-16 10:27:33 -0700247//
248// *struct {
249// Soong_config_variables struct {
250// Board struct {
251// Soc_a interface{}
252// Soc_b interface{}
253// }
254// }
255// }
256//
Colin Cross9d34f352019-11-22 16:03:51 -0800257// And whose value is:
Colin Crossd079e0b2022-08-16 10:27:33 -0700258//
259// &{
260// Soong_config_variables: {
261// Board: {
262// Soc_a: (*struct{ Cflags []string })(nil),
263// Soc_b: (*struct{ Cflags []string })(nil),
264// },
265// },
266// }
Paul Duffine8b47682023-01-09 15:42:57 +0000267func CreateProperties(factoryProps []interface{}, moduleType *ModuleType) reflect.Value {
Colin Cross9d34f352019-11-22 16:03:51 -0800268 var fields []reflect.StructField
269
Colin Cross9d34f352019-11-22 16:03:51 -0800270 affectablePropertiesType := createAffectablePropertiesType(moduleType.affectableProperties, factoryProps)
271 if affectablePropertiesType == nil {
272 return reflect.Value{}
273 }
274
275 for _, c := range moduleType.Variables {
276 fields = append(fields, reflect.StructField{
277 Name: proptools.FieldNameForProperty(c.variableProperty()),
278 Type: c.variableValuesType(),
279 })
280 }
281
282 typ := reflect.StructOf([]reflect.StructField{{
Jingwen Chena47f28d2021-11-02 16:43:57 +0000283 Name: SoongConfigProperty,
Colin Cross9d34f352019-11-22 16:03:51 -0800284 Type: reflect.StructOf(fields),
285 }})
286
287 props := reflect.New(typ)
Jingwen Chena47f28d2021-11-02 16:43:57 +0000288 structConditions := props.Elem().FieldByName(SoongConfigProperty)
Colin Cross9d34f352019-11-22 16:03:51 -0800289
290 for i, c := range moduleType.Variables {
291 c.initializeProperties(structConditions.Field(i), affectablePropertiesType)
292 }
293
294 return props
295}
296
297// createAffectablePropertiesType creates a reflect.Type of a struct that has a field for each affectable property
298// that exists in factoryProps.
299func createAffectablePropertiesType(affectableProperties []string, factoryProps []interface{}) reflect.Type {
300 affectableProperties = append([]string(nil), affectableProperties...)
301 sort.Strings(affectableProperties)
302
303 var recurse func(prefix string, aps []string) ([]string, reflect.Type)
304 recurse = func(prefix string, aps []string) ([]string, reflect.Type) {
305 var fields []reflect.StructField
306
Colin Cross997f27a2021-03-05 17:25:41 -0800307 // Iterate while the list is non-empty so it can be modified in the loop.
Colin Cross9d34f352019-11-22 16:03:51 -0800308 for len(affectableProperties) > 0 {
309 p := affectableProperties[0]
310 if !strings.HasPrefix(affectableProperties[0], prefix) {
Colin Cross997f27a2021-03-05 17:25:41 -0800311 // The properties are sorted and recurse is always called with a prefix that matches
312 // the first property in the list, so if we've reached one that doesn't match the
313 // prefix we are done with this prefix.
Colin Cross9d34f352019-11-22 16:03:51 -0800314 break
315 }
Colin Cross9d34f352019-11-22 16:03:51 -0800316
317 nestedProperty := strings.TrimPrefix(p, prefix)
318 if i := strings.IndexRune(nestedProperty, '.'); i >= 0 {
319 var nestedType reflect.Type
320 nestedPrefix := nestedProperty[:i+1]
321
Colin Cross997f27a2021-03-05 17:25:41 -0800322 // Recurse to handle the properties with the found prefix. This will return
323 // an updated affectableProperties with the handled entries removed from the front
324 // of the list, and the type that contains the handled entries. The type may be
325 // nil if none of the entries matched factoryProps.
Colin Cross9d34f352019-11-22 16:03:51 -0800326 affectableProperties, nestedType = recurse(prefix+nestedPrefix, affectableProperties)
327
328 if nestedType != nil {
329 nestedFieldName := proptools.FieldNameForProperty(strings.TrimSuffix(nestedPrefix, "."))
330
331 fields = append(fields, reflect.StructField{
332 Name: nestedFieldName,
333 Type: nestedType,
334 })
335 }
336 } else {
337 typ := typeForPropertyFromPropertyStructs(factoryProps, p)
338 if typ != nil {
339 fields = append(fields, reflect.StructField{
340 Name: proptools.FieldNameForProperty(nestedProperty),
341 Type: typ,
342 })
343 }
Colin Cross997f27a2021-03-05 17:25:41 -0800344 // The first element in the list has been handled, remove it from the list.
345 affectableProperties = affectableProperties[1:]
Colin Cross9d34f352019-11-22 16:03:51 -0800346 }
347 }
348
349 var typ reflect.Type
350 if len(fields) > 0 {
351 typ = reflect.StructOf(fields)
352 }
353 return affectableProperties, typ
354 }
355
356 affectableProperties, typ := recurse("", affectableProperties)
357 if len(affectableProperties) > 0 {
358 panic(fmt.Errorf("didn't handle all affectable properties"))
359 }
360
361 if typ != nil {
362 return reflect.PtrTo(typ)
363 }
364
365 return nil
366}
367
368func typeForPropertyFromPropertyStructs(psList []interface{}, property string) reflect.Type {
369 for _, ps := range psList {
370 if typ := typeForPropertyFromPropertyStruct(ps, property); typ != nil {
371 return typ
372 }
373 }
374
375 return nil
376}
377
378func typeForPropertyFromPropertyStruct(ps interface{}, property string) reflect.Type {
379 v := reflect.ValueOf(ps)
380 for len(property) > 0 {
381 if !v.IsValid() {
382 return nil
383 }
384
385 if v.Kind() == reflect.Interface {
386 if v.IsNil() {
387 return nil
388 } else {
389 v = v.Elem()
390 }
391 }
392
393 if v.Kind() == reflect.Ptr {
394 if v.IsNil() {
395 v = reflect.Zero(v.Type().Elem())
396 } else {
397 v = v.Elem()
398 }
399 }
400
401 if v.Kind() != reflect.Struct {
402 return nil
403 }
404
405 if index := strings.IndexRune(property, '.'); index >= 0 {
406 prefix := property[:index]
407 property = property[index+1:]
408
409 v = v.FieldByName(proptools.FieldNameForProperty(prefix))
410 } else {
411 f := v.FieldByName(proptools.FieldNameForProperty(property))
412 if !f.IsValid() {
413 return nil
414 }
415 return f.Type()
416 }
417 }
418 return nil
419}
420
421// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
422// based on SoongConfig values.
Liz Kammerfe8853d2020-12-16 09:34:33 -0800423// Expects that props contains a struct field with name soong_config_variables. The fields within
Liz Kammer432bd592020-12-16 12:42:02 -0800424// soong_config_variables are expected to be in the same order as moduleType.Variables.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700425func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
Colin Cross9d34f352019-11-22 16:03:51 -0800426 var ret []interface{}
Jingwen Chena47f28d2021-11-02 16:43:57 +0000427 props = props.Elem().FieldByName(SoongConfigProperty)
Colin Cross9d34f352019-11-22 16:03:51 -0800428 for i, c := range moduleType.Variables {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700429 if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
430 return nil, err
431 } else if ps != nil {
Colin Cross9d34f352019-11-22 16:03:51 -0800432 ret = append(ret, ps)
433 }
434 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700435 return ret, nil
Colin Cross9d34f352019-11-22 16:03:51 -0800436}
437
438type ModuleType struct {
439 BaseModuleType string
440 ConfigNamespace string
441 Variables []soongConfigVariable
442
443 affectableProperties []string
444 variableNames []string
445}
446
Liz Kammer432bd592020-12-16 12:42:02 -0800447func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
448 mt := &ModuleType{
449 affectableProperties: props.Properties,
450 ConfigNamespace: props.Config_namespace,
451 BaseModuleType: props.Module_type,
452 variableNames: props.Variables,
453 }
454
455 for _, name := range props.Bool_variables {
456 if err := checkVariableName(name); err != nil {
457 return nil, []error{fmt.Errorf("bool_variables %s", err)}
458 }
459
460 mt.Variables = append(mt.Variables, newBoolVariable(name))
461 }
462
463 for _, name := range props.Value_variables {
464 if err := checkVariableName(name); err != nil {
465 return nil, []error{fmt.Errorf("value_variables %s", err)}
466 }
467
468 mt.Variables = append(mt.Variables, &valueVariable{
469 baseVariable: baseVariable{
470 variable: name,
471 },
472 })
473 }
474
Inseob Kim02c86182024-04-05 17:52:06 +0900475 for _, name := range props.List_variables {
476 if err := checkVariableName(name); err != nil {
477 return nil, []error{fmt.Errorf("list_variables %s", err)}
478 }
479
480 mt.Variables = append(mt.Variables, &listVariable{
481 baseVariable: baseVariable{
482 variable: name,
483 },
484 })
485 }
486
Liz Kammer432bd592020-12-16 12:42:02 -0800487 return mt, nil
488}
489
490func checkVariableName(name string) error {
491 if name == "" {
492 return fmt.Errorf("name must not be blank")
493 } else if name == conditionsDefault {
494 return fmt.Errorf("%q is reserved", conditionsDefault)
495 }
496 return nil
497}
498
Colin Cross9d34f352019-11-22 16:03:51 -0800499type soongConfigVariable interface {
500 // variableProperty returns the name of the variable.
501 variableProperty() string
502
503 // conditionalValuesType returns a reflect.Type that contains an interface{} for each possible value.
504 variableValuesType() reflect.Type
505
506 // initializeProperties is passed a reflect.Value of the reflect.Type returned by conditionalValuesType and a
507 // reflect.Type of the affectable properties, and should initialize each interface{} in the reflect.Value with
508 // the zero value of the affectable properties type.
509 initializeProperties(v reflect.Value, typ reflect.Type)
510
511 // PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
512 // to the module.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700513 PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
Colin Cross9d34f352019-11-22 16:03:51 -0800514}
515
516type baseVariable struct {
517 variable string
518}
519
520func (c *baseVariable) variableProperty() string {
521 return CanonicalizeToProperty(c.variable)
522}
523
524type stringVariable struct {
525 baseVariable
526 values []string
527}
528
529func (s *stringVariable) variableValuesType() reflect.Type {
530 var fields []reflect.StructField
531
Liz Kammer432bd592020-12-16 12:42:02 -0800532 var values []string
533 values = append(values, s.values...)
534 values = append(values, conditionsDefault)
535 for _, v := range values {
Colin Cross9d34f352019-11-22 16:03:51 -0800536 fields = append(fields, reflect.StructField{
537 Name: proptools.FieldNameForProperty(v),
538 Type: emptyInterfaceType,
539 })
540 }
541
542 return reflect.StructOf(fields)
543}
544
Liz Kammer432bd592020-12-16 12:42:02 -0800545// initializeProperties initializes properties to zero value of typ for supported values and a final
546// conditions default field.
Colin Cross9d34f352019-11-22 16:03:51 -0800547func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
548 for i := range s.values {
549 v.Field(i).Set(reflect.Zero(typ))
550 }
Liz Kammer432bd592020-12-16 12:42:02 -0800551 v.Field(len(s.values)).Set(reflect.Zero(typ)) // conditions default is the final value
Colin Cross9d34f352019-11-22 16:03:51 -0800552}
553
Liz Kammer432bd592020-12-16 12:42:02 -0800554// Extracts an interface from values containing the properties to apply based on config.
555// 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 -0700556func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Cole Faustb0a91332022-11-01 17:10:23 +0000557 configValue := config.String(s.variable)
558 if configValue != "" && !InList(configValue, s.values) {
559 return nil, fmt.Errorf("Soong config property %q must be one of %v, found %q", s.variable, s.values, configValue)
560 }
Colin Cross9d34f352019-11-22 16:03:51 -0800561 for j, v := range s.values {
Liz Kammer432bd592020-12-16 12:42:02 -0800562 f := values.Field(j)
Cole Faustb0a91332022-11-01 17:10:23 +0000563 if configValue == v && !f.Elem().IsNil() {
Liz Kammer432bd592020-12-16 12:42:02 -0800564 return f.Interface(), nil
Colin Cross9d34f352019-11-22 16:03:51 -0800565 }
566 }
Liz Kammer432bd592020-12-16 12:42:02 -0800567 // if we have reached this point, we have checked all valid values of string and either:
568 // * the value was not set
569 // * the value was set but that value was not specified in the Android.bp file
570 return values.Field(len(s.values)).Interface(), nil
Colin Cross9d34f352019-11-22 16:03:51 -0800571}
572
Liz Kammer432bd592020-12-16 12:42:02 -0800573// Struct to allow conditions set based on a boolean variable
Colin Cross9d34f352019-11-22 16:03:51 -0800574type boolVariable struct {
575 baseVariable
576}
577
Liz Kammer432bd592020-12-16 12:42:02 -0800578// newBoolVariable constructs a boolVariable with the given name
Liz Kammerfe8853d2020-12-16 09:34:33 -0800579func newBoolVariable(name string) *boolVariable {
580 return &boolVariable{
581 baseVariable{
582 variable: name,
583 },
584 }
585}
586
Colin Cross9d34f352019-11-22 16:03:51 -0800587func (b boolVariable) variableValuesType() reflect.Type {
588 return emptyInterfaceType
589}
590
Liz Kammer432bd592020-12-16 12:42:02 -0800591// initializeProperties initializes a property to zero value of typ with an additional conditions
592// default field.
Colin Cross9d34f352019-11-22 16:03:51 -0800593func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
Liz Kammer432bd592020-12-16 12:42:02 -0800594 initializePropertiesWithDefault(v, typ)
Colin Cross9d34f352019-11-22 16:03:51 -0800595}
596
Liz Kammer432bd592020-12-16 12:42:02 -0800597// initializePropertiesWithDefault, initialize with zero value, v to contain a field for each field
598// in typ, with an additional field for defaults of type typ. This should be used to initialize
599// boolVariable, valueVariable, or any future implementations of soongConfigVariable which support
600// one variable and a default.
601func initializePropertiesWithDefault(v reflect.Value, typ reflect.Type) {
602 sTyp := typ.Elem()
603 var fields []reflect.StructField
604 for i := 0; i < sTyp.NumField(); i++ {
605 fields = append(fields, sTyp.Field(i))
Colin Cross9d34f352019-11-22 16:03:51 -0800606 }
607
Liz Kammer432bd592020-12-16 12:42:02 -0800608 // create conditions_default field
609 nestedFieldName := proptools.FieldNameForProperty(conditionsDefault)
610 fields = append(fields, reflect.StructField{
611 Name: nestedFieldName,
612 Type: typ,
613 })
614
615 newTyp := reflect.PtrTo(reflect.StructOf(fields))
616 v.Set(reflect.Zero(newTyp))
617}
618
619// conditionsDefaultField extracts the conditions_default field from v. This is always the final
620// field if initialized with initializePropertiesWithDefault.
621func conditionsDefaultField(v reflect.Value) reflect.Value {
622 return v.Field(v.NumField() - 1)
623}
624
625// removeDefault removes the conditions_default field from values while retaining values from all
626// other fields. This allows
627func removeDefault(values reflect.Value) reflect.Value {
628 v := values.Elem().Elem()
629 s := conditionsDefaultField(v)
630 // if conditions_default field was not set, there will be no issues extending properties.
631 if !s.IsValid() {
632 return v
633 }
634
635 // If conditions_default field was set, it has the correct type for our property. Create a new
636 // reflect.Value of the conditions_default type and copy all fields (except for
637 // conditions_default) based on values to the result.
638 res := reflect.New(s.Type().Elem())
639 for i := 0; i < res.Type().Elem().NumField(); i++ {
640 val := v.Field(i)
641 res.Elem().Field(i).Set(val)
642 }
643
644 return res
645}
646
647// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
648// the module. If the value was not set, conditions_default interface will be returned; otherwise,
649// the interface in values, without conditions_default will be returned.
650func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
651 // If this variable was not referenced in the module, there are no properties to apply.
652 if values.Elem().IsZero() {
653 return nil, nil
654 }
655 if config.Bool(b.variable) {
656 values = removeDefault(values)
657 return values.Interface(), nil
658 }
659 v := values.Elem().Elem()
660 if f := conditionsDefaultField(v); f.IsValid() {
661 return f.Interface(), nil
662 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700663 return nil, nil
664}
665
Liz Kammer432bd592020-12-16 12:42:02 -0800666// Struct to allow conditions set based on a value variable, supporting string substitution.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700667type valueVariable struct {
668 baseVariable
669}
670
671func (s *valueVariable) 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.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700677func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
Liz Kammer432bd592020-12-16 12:42:02 -0800678 initializePropertiesWithDefault(v, typ)
Dan Willemsenb0935db2020-03-23 19:42:18 -0700679}
680
Liz Kammer432bd592020-12-16 12:42:02 -0800681// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
682// the module. If the variable was not set, conditions_default interface will be returned;
683// otherwise, the interface in values, without conditions_default will be returned with all
684// appropriate string substitutions based on variable being set.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700685func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Liz Kammer432bd592020-12-16 12:42:02 -0800686 // If this variable was not referenced in the module, there are no properties to apply.
687 if !values.IsValid() || values.Elem().IsZero() {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700688 return nil, nil
689 }
Liz Kammer432bd592020-12-16 12:42:02 -0800690 if !config.IsSet(s.variable) {
691 return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
692 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700693 configValue := config.String(s.variable)
694
Liz Kammer432bd592020-12-16 12:42:02 -0800695 values = removeDefault(values)
696 propStruct := values.Elem()
Liz Kammer40ddfaa2020-12-16 10:59:00 -0800697 if !propStruct.IsValid() {
698 return nil, nil
699 }
Cole Faust1da0b202024-02-21 10:50:33 -0800700 if err := s.printfIntoPropertyRecursive(nil, propStruct, configValue); err != nil {
701 return nil, err
702 }
703
704 return values.Interface(), nil
705}
706
707func (s *valueVariable) printfIntoPropertyRecursive(fieldName []string, propStruct reflect.Value, configValue string) error {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700708 for i := 0; i < propStruct.NumField(); i++ {
709 field := propStruct.Field(i)
710 kind := field.Kind()
711 if kind == reflect.Ptr {
712 if field.IsNil() {
713 continue
714 }
715 field = field.Elem()
Cole Fausta03ac3a2024-01-12 12:12:26 -0800716 kind = field.Kind()
Dan Willemsenb0935db2020-03-23 19:42:18 -0700717 }
718 switch kind {
719 case reflect.String:
720 err := printfIntoProperty(field, configValue)
721 if err != nil {
Cole Faust1da0b202024-02-21 10:50:33 -0800722 fieldName = append(fieldName, propStruct.Type().Field(i).Name)
723 return fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, strings.Join(fieldName, "."), err)
Dan Willemsenb0935db2020-03-23 19:42:18 -0700724 }
725 case reflect.Slice:
726 for j := 0; j < field.Len(); j++ {
727 err := printfIntoProperty(field.Index(j), configValue)
728 if err != nil {
Cole Faust1da0b202024-02-21 10:50:33 -0800729 fieldName = append(fieldName, propStruct.Type().Field(i).Name)
730 return fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, strings.Join(fieldName, "."), err)
Dan Willemsenb0935db2020-03-23 19:42:18 -0700731 }
732 }
733 case reflect.Bool:
734 // Nothing to do
Cole Faust1da0b202024-02-21 10:50:33 -0800735 case reflect.Struct:
Cole Faust5f297062024-05-22 14:30:16 -0700736 if proptools.IsConfigurable(field.Type()) {
737 if err := proptools.PrintfIntoConfigurable(field.Interface(), configValue); err != nil {
738 fieldName = append(fieldName, propStruct.Type().Field(i).Name)
739 return fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, strings.Join(fieldName, "."), err)
740 }
741 } else {
742 fieldName = append(fieldName, propStruct.Type().Field(i).Name)
743 if err := s.printfIntoPropertyRecursive(fieldName, field, configValue); err != nil {
744 return err
745 }
746 fieldName = fieldName[:len(fieldName)-1]
Cole Faust1da0b202024-02-21 10:50:33 -0800747 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700748 default:
Cole Faust1da0b202024-02-21 10:50:33 -0800749 fieldName = append(fieldName, propStruct.Type().Field(i).Name)
750 return fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, strings.Join(fieldName, "."), kind)
Dan Willemsenb0935db2020-03-23 19:42:18 -0700751 }
752 }
Cole Faust1da0b202024-02-21 10:50:33 -0800753 return nil
Dan Willemsenb0935db2020-03-23 19:42:18 -0700754}
755
Inseob Kim02c86182024-04-05 17:52:06 +0900756// Struct to allow conditions set based on a list variable, supporting string substitution.
757type listVariable struct {
758 baseVariable
759}
760
761func (s *listVariable) variableValuesType() reflect.Type {
762 return emptyInterfaceType
763}
764
765// initializeProperties initializes a property to zero value of typ with an additional conditions
766// default field.
767func (s *listVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
768 initializePropertiesWithDefault(v, typ)
769}
770
771// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
772// the module. If the variable was not set, conditions_default interface will be returned;
773// otherwise, the interface in values, without conditions_default will be returned with all
774// appropriate string substitutions based on variable being set.
775func (s *listVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
776 // If this variable was not referenced in the module, there are no properties to apply.
777 if !values.IsValid() || values.Elem().IsZero() {
778 return nil, nil
779 }
780 if !config.IsSet(s.variable) {
781 return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
782 }
783 configValues := strings.Split(config.String(s.variable), " ")
784
785 values = removeDefault(values)
786 propStruct := values.Elem()
787 if !propStruct.IsValid() {
788 return nil, nil
789 }
790 if err := s.printfIntoPropertyRecursive(nil, propStruct, configValues); err != nil {
791 return nil, err
792 }
793
794 return values.Interface(), nil
795}
796
797func (s *listVariable) printfIntoPropertyRecursive(fieldName []string, propStruct reflect.Value, configValues []string) error {
798 for i := 0; i < propStruct.NumField(); i++ {
799 field := propStruct.Field(i)
800 kind := field.Kind()
801 if kind == reflect.Ptr {
802 if field.IsNil() {
803 continue
804 }
805 field = field.Elem()
806 kind = field.Kind()
807 }
808 switch kind {
809 case reflect.Slice:
810 elemType := field.Type().Elem()
811 newLen := field.Len() * len(configValues)
812 newField := reflect.MakeSlice(field.Type(), 0, newLen)
813 for j := 0; j < field.Len(); j++ {
814 for _, configValue := range configValues {
815 res := reflect.Indirect(reflect.New(elemType))
816 res.Set(field.Index(j))
817 err := printfIntoProperty(res, configValue)
818 if err != nil {
819 fieldName = append(fieldName, propStruct.Type().Field(i).Name)
820 return fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, strings.Join(fieldName, "."), err)
821 }
822 newField = reflect.Append(newField, res)
823 }
824 }
825 field.Set(newField)
826 case reflect.Struct:
Cole Faust43ddd082024-06-17 12:32:40 -0700827 if proptools.IsConfigurable(field.Type()) {
828 fieldName = append(fieldName, propStruct.Type().Field(i).Name)
829 return fmt.Errorf("soong_config_variables.%s.%s: list variables are not supported on configurable properties", s.variable, strings.Join(fieldName, "."))
830 } else {
831 fieldName = append(fieldName, propStruct.Type().Field(i).Name)
832 if err := s.printfIntoPropertyRecursive(fieldName, field, configValues); err != nil {
833 return err
834 }
835 fieldName = fieldName[:len(fieldName)-1]
Inseob Kim02c86182024-04-05 17:52:06 +0900836 }
Inseob Kim02c86182024-04-05 17:52:06 +0900837 default:
838 fieldName = append(fieldName, propStruct.Type().Field(i).Name)
839 return fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, strings.Join(fieldName, "."), kind)
840 }
841 }
842 return nil
843}
844
Dan Willemsenb0935db2020-03-23 19:42:18 -0700845func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
846 s := propertyValue.String()
847
848 count := strings.Count(s, "%")
849 if count == 0 {
850 return nil
851 }
852
853 if count > 1 {
Inseob Kim02c86182024-04-05 17:52:06 +0900854 return fmt.Errorf("list/value variable properties only support a single '%%'")
Dan Willemsenb0935db2020-03-23 19:42:18 -0700855 }
856
857 if !strings.Contains(s, "%s") {
858 return fmt.Errorf("unsupported %% in value variable property")
859 }
860
861 propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, configValue)))
862
Colin Cross9d34f352019-11-22 16:03:51 -0800863 return nil
864}
865
866func CanonicalizeToProperty(v string) string {
867 return strings.Map(func(r rune) rune {
868 switch {
869 case r >= 'A' && r <= 'Z',
870 r >= 'a' && r <= 'z',
871 r >= '0' && r <= '9',
872 r == '_':
873 return r
874 default:
875 return '_'
876 }
877 }, v)
878}
879
880func CanonicalizeToProperties(values []string) []string {
881 ret := make([]string, len(values))
882 for i, v := range values {
883 ret[i] = CanonicalizeToProperty(v)
884 }
885 return ret
886}
887
888type emptyInterfaceStruct struct {
889 i interface{}
890}
891
892var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type
Cole Faustb0a91332022-11-01 17:10:23 +0000893
894// InList checks if the string belongs to the list
895func InList(s string, list []string) bool {
896 for _, s2 := range list {
897 if s2 == s {
898 return true
899 }
900 }
901 return false
902}