blob: f6b49382cca1689394fa5808702ff05e0f6aaf75 [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"
Colin Cross3cc31542024-01-04 14:47:45 -080019 "github.com/google/blueprint/parser"
20 "github.com/google/blueprint/proptools"
Colin Cross9d34f352019-11-22 16:03:51 -080021 "io"
22 "reflect"
23 "sort"
24 "strings"
Colin Cross9d34f352019-11-22 16:03:51 -080025)
26
Liz Kammer432bd592020-12-16 12:42:02 -080027const conditionsDefault = "conditions_default"
28
Jingwen Chena47f28d2021-11-02 16:43:57 +000029var SoongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
Colin Cross9d34f352019-11-22 16:03:51 -080030
31// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
32// result so each file is only parsed once.
33func Parse(r io.Reader, from string) (*SoongConfigDefinition, []error) {
34 scope := parser.NewScope(nil)
35 file, errs := parser.ParseAndEval(from, r, scope)
36
37 if len(errs) > 0 {
38 return nil, errs
39 }
40
41 mtDef := &SoongConfigDefinition{
42 ModuleTypes: make(map[string]*ModuleType),
43 variables: make(map[string]soongConfigVariable),
44 }
45
46 for _, def := range file.Defs {
47 switch def := def.(type) {
48 case *parser.Module:
49 newErrs := processImportModuleDef(mtDef, def)
50
51 if len(newErrs) > 0 {
52 errs = append(errs, newErrs...)
53 }
54
55 case *parser.Assignment:
56 // Already handled via Scope object
57 default:
58 panic("unknown definition type")
59 }
60 }
61
62 if len(errs) > 0 {
63 return nil, errs
64 }
65
66 for name, moduleType := range mtDef.ModuleTypes {
67 for _, varName := range moduleType.variableNames {
68 if v, ok := mtDef.variables[varName]; ok {
69 moduleType.Variables = append(moduleType.Variables, v)
70 } else {
71 return nil, []error{
72 fmt.Errorf("unknown variable %q in module type %q", varName, name),
73 }
74 }
75 }
76 }
77
78 return mtDef, nil
79}
80
81func processImportModuleDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
82 switch def.Type {
83 case "soong_config_module_type":
84 return processModuleTypeDef(v, def)
85 case "soong_config_string_variable":
86 return processStringVariableDef(v, def)
87 case "soong_config_bool_variable":
88 return processBoolVariableDef(v, def)
89 default:
90 // Unknown module types will be handled when the file is parsed as a normal
91 // Android.bp file.
92 }
93
94 return nil
95}
96
97type ModuleTypeProperties struct {
98 // the name of the new module type. Unlike most modules, this name does not need to be unique,
99 // although only one module type with any name will be importable into an Android.bp file.
100 Name string
101
102 // the module type that this module type will extend.
103 Module_type string
104
105 // the SOONG_CONFIG_NAMESPACE value from a BoardConfig.mk that this module type will read
106 // configuration variables from.
107 Config_namespace string
108
109 // the list of SOONG_CONFIG variables that this module type will read
110 Variables []string
111
Dan Willemsen2b8b89c2020-03-23 19:39:34 -0700112 // the list of boolean SOONG_CONFIG variables that this module type will read
113 Bool_variables []string
114
Dan Willemsenb0935db2020-03-23 19:42:18 -0700115 // the list of SOONG_CONFIG variables that this module type will read. The value will be
116 // inserted into the properties with %s substitution.
117 Value_variables []string
118
Colin Cross9d34f352019-11-22 16:03:51 -0800119 // the list of properties that this module type will extend.
120 Properties []string
121}
122
123func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
124
125 props := &ModuleTypeProperties{}
126
127 _, errs = proptools.UnpackProperties(def.Properties, props)
128 if len(errs) > 0 {
129 return errs
130 }
131
132 if props.Name == "" {
133 errs = append(errs, fmt.Errorf("name property must be set"))
134 }
135
136 if props.Config_namespace == "" {
137 errs = append(errs, fmt.Errorf("config_namespace property must be set"))
138 }
139
140 if props.Module_type == "" {
141 errs = append(errs, fmt.Errorf("module_type property must be set"))
142 }
143
144 if len(errs) > 0 {
145 return errs
146 }
147
Liz Kammer432bd592020-12-16 12:42:02 -0800148 if mt, errs := newModuleType(props); len(errs) > 0 {
149 return errs
150 } else {
151 v.ModuleTypes[props.Name] = mt
Dan Willemsenb0935db2020-03-23 19:42:18 -0700152 }
153
Colin Cross9d34f352019-11-22 16:03:51 -0800154 return nil
155}
156
157type VariableProperties struct {
158 Name string
159}
160
161type StringVariableProperties struct {
162 Values []string
163}
164
165func processStringVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
166 stringProps := &StringVariableProperties{}
167
168 base, errs := processVariableDef(def, stringProps)
169 if len(errs) > 0 {
170 return errs
171 }
172
173 if len(stringProps.Values) == 0 {
174 return []error{fmt.Errorf("values property must be set")}
175 }
176
Liz Kammer72beb342022-02-03 08:42:10 -0500177 vals := make(map[string]bool, len(stringProps.Values))
Liz Kammer432bd592020-12-16 12:42:02 -0800178 for _, name := range stringProps.Values {
179 if err := checkVariableName(name); err != nil {
180 return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
Liz Kammer72beb342022-02-03 08:42:10 -0500181 } else if _, ok := vals[name]; ok {
182 return []error{fmt.Errorf("soong_config_string_variable: values property error: duplicate value: %q", name)}
Liz Kammer432bd592020-12-16 12:42:02 -0800183 }
Liz Kammer72beb342022-02-03 08:42:10 -0500184 vals[name] = true
Liz Kammer432bd592020-12-16 12:42:02 -0800185 }
186
Colin Cross9d34f352019-11-22 16:03:51 -0800187 v.variables[base.variable] = &stringVariable{
188 baseVariable: base,
189 values: CanonicalizeToProperties(stringProps.Values),
190 }
191
192 return nil
193}
194
195func processBoolVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
196 base, errs := processVariableDef(def)
197 if len(errs) > 0 {
198 return errs
199 }
200
201 v.variables[base.variable] = &boolVariable{
202 baseVariable: base,
203 }
204
205 return nil
206}
207
208func processVariableDef(def *parser.Module,
209 extraProps ...interface{}) (cond baseVariable, errs []error) {
210
211 props := &VariableProperties{}
212
213 allProps := append([]interface{}{props}, extraProps...)
214
215 _, errs = proptools.UnpackProperties(def.Properties, allProps...)
216 if len(errs) > 0 {
217 return baseVariable{}, errs
218 }
219
220 if props.Name == "" {
221 return baseVariable{}, []error{fmt.Errorf("name property must be set")}
222 }
223
224 return baseVariable{
225 variable: props.Name,
226 }, nil
227}
228
229type SoongConfigDefinition struct {
230 ModuleTypes map[string]*ModuleType
231
232 variables map[string]soongConfigVariable
233}
234
235// CreateProperties returns a reflect.Value of a newly constructed type that contains the desired
236// property layout for the Soong config variables, with each possible value an interface{} that
237// contains a nil pointer to another newly constructed type that contains the affectable properties.
238// The reflect.Value will be cloned for each call to the Soong config module type's factory method.
239//
240// For example, the acme_cc_defaults example above would
241// produce a reflect.Value whose type is:
Colin Crossd079e0b2022-08-16 10:27:33 -0700242//
243// *struct {
244// Soong_config_variables struct {
245// Board struct {
246// Soc_a interface{}
247// Soc_b interface{}
248// }
249// }
250// }
251//
Colin Cross9d34f352019-11-22 16:03:51 -0800252// And whose value is:
Colin Crossd079e0b2022-08-16 10:27:33 -0700253//
254// &{
255// Soong_config_variables: {
256// Board: {
257// Soc_a: (*struct{ Cflags []string })(nil),
258// Soc_b: (*struct{ Cflags []string })(nil),
259// },
260// },
261// }
Paul Duffine8b47682023-01-09 15:42:57 +0000262func CreateProperties(factoryProps []interface{}, moduleType *ModuleType) reflect.Value {
Colin Cross9d34f352019-11-22 16:03:51 -0800263 var fields []reflect.StructField
264
Colin Cross9d34f352019-11-22 16:03:51 -0800265 affectablePropertiesType := createAffectablePropertiesType(moduleType.affectableProperties, factoryProps)
266 if affectablePropertiesType == nil {
267 return reflect.Value{}
268 }
269
270 for _, c := range moduleType.Variables {
271 fields = append(fields, reflect.StructField{
272 Name: proptools.FieldNameForProperty(c.variableProperty()),
273 Type: c.variableValuesType(),
274 })
275 }
276
277 typ := reflect.StructOf([]reflect.StructField{{
Jingwen Chena47f28d2021-11-02 16:43:57 +0000278 Name: SoongConfigProperty,
Colin Cross9d34f352019-11-22 16:03:51 -0800279 Type: reflect.StructOf(fields),
280 }})
281
282 props := reflect.New(typ)
Jingwen Chena47f28d2021-11-02 16:43:57 +0000283 structConditions := props.Elem().FieldByName(SoongConfigProperty)
Colin Cross9d34f352019-11-22 16:03:51 -0800284
285 for i, c := range moduleType.Variables {
286 c.initializeProperties(structConditions.Field(i), affectablePropertiesType)
287 }
288
289 return props
290}
291
292// createAffectablePropertiesType creates a reflect.Type of a struct that has a field for each affectable property
293// that exists in factoryProps.
294func createAffectablePropertiesType(affectableProperties []string, factoryProps []interface{}) reflect.Type {
295 affectableProperties = append([]string(nil), affectableProperties...)
296 sort.Strings(affectableProperties)
297
298 var recurse func(prefix string, aps []string) ([]string, reflect.Type)
299 recurse = func(prefix string, aps []string) ([]string, reflect.Type) {
300 var fields []reflect.StructField
301
Colin Cross997f27a2021-03-05 17:25:41 -0800302 // Iterate while the list is non-empty so it can be modified in the loop.
Colin Cross9d34f352019-11-22 16:03:51 -0800303 for len(affectableProperties) > 0 {
304 p := affectableProperties[0]
305 if !strings.HasPrefix(affectableProperties[0], prefix) {
Colin Cross997f27a2021-03-05 17:25:41 -0800306 // The properties are sorted and recurse is always called with a prefix that matches
307 // the first property in the list, so if we've reached one that doesn't match the
308 // prefix we are done with this prefix.
Colin Cross9d34f352019-11-22 16:03:51 -0800309 break
310 }
Colin Cross9d34f352019-11-22 16:03:51 -0800311
312 nestedProperty := strings.TrimPrefix(p, prefix)
313 if i := strings.IndexRune(nestedProperty, '.'); i >= 0 {
314 var nestedType reflect.Type
315 nestedPrefix := nestedProperty[:i+1]
316
Colin Cross997f27a2021-03-05 17:25:41 -0800317 // Recurse to handle the properties with the found prefix. This will return
318 // an updated affectableProperties with the handled entries removed from the front
319 // of the list, and the type that contains the handled entries. The type may be
320 // nil if none of the entries matched factoryProps.
Colin Cross9d34f352019-11-22 16:03:51 -0800321 affectableProperties, nestedType = recurse(prefix+nestedPrefix, affectableProperties)
322
323 if nestedType != nil {
324 nestedFieldName := proptools.FieldNameForProperty(strings.TrimSuffix(nestedPrefix, "."))
325
326 fields = append(fields, reflect.StructField{
327 Name: nestedFieldName,
328 Type: nestedType,
329 })
330 }
331 } else {
332 typ := typeForPropertyFromPropertyStructs(factoryProps, p)
333 if typ != nil {
334 fields = append(fields, reflect.StructField{
335 Name: proptools.FieldNameForProperty(nestedProperty),
336 Type: typ,
337 })
338 }
Colin Cross997f27a2021-03-05 17:25:41 -0800339 // The first element in the list has been handled, remove it from the list.
340 affectableProperties = affectableProperties[1:]
Colin Cross9d34f352019-11-22 16:03:51 -0800341 }
342 }
343
344 var typ reflect.Type
345 if len(fields) > 0 {
346 typ = reflect.StructOf(fields)
347 }
348 return affectableProperties, typ
349 }
350
351 affectableProperties, typ := recurse("", affectableProperties)
352 if len(affectableProperties) > 0 {
353 panic(fmt.Errorf("didn't handle all affectable properties"))
354 }
355
356 if typ != nil {
357 return reflect.PtrTo(typ)
358 }
359
360 return nil
361}
362
363func typeForPropertyFromPropertyStructs(psList []interface{}, property string) reflect.Type {
364 for _, ps := range psList {
365 if typ := typeForPropertyFromPropertyStruct(ps, property); typ != nil {
366 return typ
367 }
368 }
369
370 return nil
371}
372
373func typeForPropertyFromPropertyStruct(ps interface{}, property string) reflect.Type {
374 v := reflect.ValueOf(ps)
375 for len(property) > 0 {
376 if !v.IsValid() {
377 return nil
378 }
379
380 if v.Kind() == reflect.Interface {
381 if v.IsNil() {
382 return nil
383 } else {
384 v = v.Elem()
385 }
386 }
387
388 if v.Kind() == reflect.Ptr {
389 if v.IsNil() {
390 v = reflect.Zero(v.Type().Elem())
391 } else {
392 v = v.Elem()
393 }
394 }
395
396 if v.Kind() != reflect.Struct {
397 return nil
398 }
399
400 if index := strings.IndexRune(property, '.'); index >= 0 {
401 prefix := property[:index]
402 property = property[index+1:]
403
404 v = v.FieldByName(proptools.FieldNameForProperty(prefix))
405 } else {
406 f := v.FieldByName(proptools.FieldNameForProperty(property))
407 if !f.IsValid() {
408 return nil
409 }
410 return f.Type()
411 }
412 }
413 return nil
414}
415
416// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
417// based on SoongConfig values.
Liz Kammerfe8853d2020-12-16 09:34:33 -0800418// Expects that props contains a struct field with name soong_config_variables. The fields within
Liz Kammer432bd592020-12-16 12:42:02 -0800419// soong_config_variables are expected to be in the same order as moduleType.Variables.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700420func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
Colin Cross9d34f352019-11-22 16:03:51 -0800421 var ret []interface{}
Jingwen Chena47f28d2021-11-02 16:43:57 +0000422 props = props.Elem().FieldByName(SoongConfigProperty)
Colin Cross9d34f352019-11-22 16:03:51 -0800423 for i, c := range moduleType.Variables {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700424 if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
425 return nil, err
426 } else if ps != nil {
Colin Cross9d34f352019-11-22 16:03:51 -0800427 ret = append(ret, ps)
428 }
429 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700430 return ret, nil
Colin Cross9d34f352019-11-22 16:03:51 -0800431}
432
433type ModuleType struct {
434 BaseModuleType string
435 ConfigNamespace string
436 Variables []soongConfigVariable
437
438 affectableProperties []string
439 variableNames []string
440}
441
Liz Kammer432bd592020-12-16 12:42:02 -0800442func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
443 mt := &ModuleType{
444 affectableProperties: props.Properties,
445 ConfigNamespace: props.Config_namespace,
446 BaseModuleType: props.Module_type,
447 variableNames: props.Variables,
448 }
449
450 for _, name := range props.Bool_variables {
451 if err := checkVariableName(name); err != nil {
452 return nil, []error{fmt.Errorf("bool_variables %s", err)}
453 }
454
455 mt.Variables = append(mt.Variables, newBoolVariable(name))
456 }
457
458 for _, name := range props.Value_variables {
459 if err := checkVariableName(name); err != nil {
460 return nil, []error{fmt.Errorf("value_variables %s", err)}
461 }
462
463 mt.Variables = append(mt.Variables, &valueVariable{
464 baseVariable: baseVariable{
465 variable: name,
466 },
467 })
468 }
469
470 return mt, nil
471}
472
473func checkVariableName(name string) error {
474 if name == "" {
475 return fmt.Errorf("name must not be blank")
476 } else if name == conditionsDefault {
477 return fmt.Errorf("%q is reserved", conditionsDefault)
478 }
479 return nil
480}
481
Colin Cross9d34f352019-11-22 16:03:51 -0800482type soongConfigVariable interface {
483 // variableProperty returns the name of the variable.
484 variableProperty() string
485
486 // conditionalValuesType returns a reflect.Type that contains an interface{} for each possible value.
487 variableValuesType() reflect.Type
488
489 // initializeProperties is passed a reflect.Value of the reflect.Type returned by conditionalValuesType and a
490 // reflect.Type of the affectable properties, and should initialize each interface{} in the reflect.Value with
491 // the zero value of the affectable properties type.
492 initializeProperties(v reflect.Value, typ reflect.Type)
493
494 // PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
495 // to the module.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700496 PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
Colin Cross9d34f352019-11-22 16:03:51 -0800497}
498
499type baseVariable struct {
500 variable string
501}
502
503func (c *baseVariable) variableProperty() string {
504 return CanonicalizeToProperty(c.variable)
505}
506
507type stringVariable struct {
508 baseVariable
509 values []string
510}
511
512func (s *stringVariable) variableValuesType() reflect.Type {
513 var fields []reflect.StructField
514
Liz Kammer432bd592020-12-16 12:42:02 -0800515 var values []string
516 values = append(values, s.values...)
517 values = append(values, conditionsDefault)
518 for _, v := range values {
Colin Cross9d34f352019-11-22 16:03:51 -0800519 fields = append(fields, reflect.StructField{
520 Name: proptools.FieldNameForProperty(v),
521 Type: emptyInterfaceType,
522 })
523 }
524
525 return reflect.StructOf(fields)
526}
527
Liz Kammer432bd592020-12-16 12:42:02 -0800528// initializeProperties initializes properties to zero value of typ for supported values and a final
529// conditions default field.
Colin Cross9d34f352019-11-22 16:03:51 -0800530func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
531 for i := range s.values {
532 v.Field(i).Set(reflect.Zero(typ))
533 }
Liz Kammer432bd592020-12-16 12:42:02 -0800534 v.Field(len(s.values)).Set(reflect.Zero(typ)) // conditions default is the final value
Colin Cross9d34f352019-11-22 16:03:51 -0800535}
536
Liz Kammer432bd592020-12-16 12:42:02 -0800537// Extracts an interface from values containing the properties to apply based on config.
538// 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 -0700539func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Cole Faustb0a91332022-11-01 17:10:23 +0000540 configValue := config.String(s.variable)
541 if configValue != "" && !InList(configValue, s.values) {
542 return nil, fmt.Errorf("Soong config property %q must be one of %v, found %q", s.variable, s.values, configValue)
543 }
Colin Cross9d34f352019-11-22 16:03:51 -0800544 for j, v := range s.values {
Liz Kammer432bd592020-12-16 12:42:02 -0800545 f := values.Field(j)
Cole Faustb0a91332022-11-01 17:10:23 +0000546 if configValue == v && !f.Elem().IsNil() {
Liz Kammer432bd592020-12-16 12:42:02 -0800547 return f.Interface(), nil
Colin Cross9d34f352019-11-22 16:03:51 -0800548 }
549 }
Liz Kammer432bd592020-12-16 12:42:02 -0800550 // if we have reached this point, we have checked all valid values of string and either:
551 // * the value was not set
552 // * the value was set but that value was not specified in the Android.bp file
553 return values.Field(len(s.values)).Interface(), nil
Colin Cross9d34f352019-11-22 16:03:51 -0800554}
555
Liz Kammer432bd592020-12-16 12:42:02 -0800556// Struct to allow conditions set based on a boolean variable
Colin Cross9d34f352019-11-22 16:03:51 -0800557type boolVariable struct {
558 baseVariable
559}
560
Liz Kammer432bd592020-12-16 12:42:02 -0800561// newBoolVariable constructs a boolVariable with the given name
Liz Kammerfe8853d2020-12-16 09:34:33 -0800562func newBoolVariable(name string) *boolVariable {
563 return &boolVariable{
564 baseVariable{
565 variable: name,
566 },
567 }
568}
569
Colin Cross9d34f352019-11-22 16:03:51 -0800570func (b boolVariable) variableValuesType() reflect.Type {
571 return emptyInterfaceType
572}
573
Liz Kammer432bd592020-12-16 12:42:02 -0800574// initializeProperties initializes a property to zero value of typ with an additional conditions
575// default field.
Colin Cross9d34f352019-11-22 16:03:51 -0800576func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
Liz Kammer432bd592020-12-16 12:42:02 -0800577 initializePropertiesWithDefault(v, typ)
Colin Cross9d34f352019-11-22 16:03:51 -0800578}
579
Liz Kammer432bd592020-12-16 12:42:02 -0800580// initializePropertiesWithDefault, initialize with zero value, v to contain a field for each field
581// in typ, with an additional field for defaults of type typ. This should be used to initialize
582// boolVariable, valueVariable, or any future implementations of soongConfigVariable which support
583// one variable and a default.
584func initializePropertiesWithDefault(v reflect.Value, typ reflect.Type) {
585 sTyp := typ.Elem()
586 var fields []reflect.StructField
587 for i := 0; i < sTyp.NumField(); i++ {
588 fields = append(fields, sTyp.Field(i))
Colin Cross9d34f352019-11-22 16:03:51 -0800589 }
590
Liz Kammer432bd592020-12-16 12:42:02 -0800591 // create conditions_default field
592 nestedFieldName := proptools.FieldNameForProperty(conditionsDefault)
593 fields = append(fields, reflect.StructField{
594 Name: nestedFieldName,
595 Type: typ,
596 })
597
598 newTyp := reflect.PtrTo(reflect.StructOf(fields))
599 v.Set(reflect.Zero(newTyp))
600}
601
602// conditionsDefaultField extracts the conditions_default field from v. This is always the final
603// field if initialized with initializePropertiesWithDefault.
604func conditionsDefaultField(v reflect.Value) reflect.Value {
605 return v.Field(v.NumField() - 1)
606}
607
608// removeDefault removes the conditions_default field from values while retaining values from all
609// other fields. This allows
610func removeDefault(values reflect.Value) reflect.Value {
611 v := values.Elem().Elem()
612 s := conditionsDefaultField(v)
613 // if conditions_default field was not set, there will be no issues extending properties.
614 if !s.IsValid() {
615 return v
616 }
617
618 // If conditions_default field was set, it has the correct type for our property. Create a new
619 // reflect.Value of the conditions_default type and copy all fields (except for
620 // conditions_default) based on values to the result.
621 res := reflect.New(s.Type().Elem())
622 for i := 0; i < res.Type().Elem().NumField(); i++ {
623 val := v.Field(i)
624 res.Elem().Field(i).Set(val)
625 }
626
627 return res
628}
629
630// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
631// the module. If the value was not set, conditions_default interface will be returned; otherwise,
632// the interface in values, without conditions_default will be returned.
633func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
634 // If this variable was not referenced in the module, there are no properties to apply.
635 if values.Elem().IsZero() {
636 return nil, nil
637 }
638 if config.Bool(b.variable) {
639 values = removeDefault(values)
640 return values.Interface(), nil
641 }
642 v := values.Elem().Elem()
643 if f := conditionsDefaultField(v); f.IsValid() {
644 return f.Interface(), nil
645 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700646 return nil, nil
647}
648
Liz Kammer432bd592020-12-16 12:42:02 -0800649// Struct to allow conditions set based on a value variable, supporting string substitution.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700650type valueVariable struct {
651 baseVariable
652}
653
654func (s *valueVariable) variableValuesType() reflect.Type {
655 return emptyInterfaceType
656}
657
Liz Kammer432bd592020-12-16 12:42:02 -0800658// initializeProperties initializes a property to zero value of typ with an additional conditions
659// default field.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700660func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
Liz Kammer432bd592020-12-16 12:42:02 -0800661 initializePropertiesWithDefault(v, typ)
Dan Willemsenb0935db2020-03-23 19:42:18 -0700662}
663
Liz Kammer432bd592020-12-16 12:42:02 -0800664// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
665// the module. If the variable was not set, conditions_default interface will be returned;
666// otherwise, the interface in values, without conditions_default will be returned with all
667// appropriate string substitutions based on variable being set.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700668func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Liz Kammer432bd592020-12-16 12:42:02 -0800669 // If this variable was not referenced in the module, there are no properties to apply.
670 if !values.IsValid() || values.Elem().IsZero() {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700671 return nil, nil
672 }
Liz Kammer432bd592020-12-16 12:42:02 -0800673 if !config.IsSet(s.variable) {
674 return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
675 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700676 configValue := config.String(s.variable)
677
Liz Kammer432bd592020-12-16 12:42:02 -0800678 values = removeDefault(values)
679 propStruct := values.Elem()
Liz Kammer40ddfaa2020-12-16 10:59:00 -0800680 if !propStruct.IsValid() {
681 return nil, nil
682 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700683 for i := 0; i < propStruct.NumField(); i++ {
684 field := propStruct.Field(i)
685 kind := field.Kind()
686 if kind == reflect.Ptr {
687 if field.IsNil() {
688 continue
689 }
690 field = field.Elem()
691 }
692 switch kind {
693 case reflect.String:
694 err := printfIntoProperty(field, configValue)
695 if err != nil {
696 return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
697 }
698 case reflect.Slice:
699 for j := 0; j < field.Len(); j++ {
700 err := printfIntoProperty(field.Index(j), configValue)
701 if err != nil {
702 return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
703 }
704 }
705 case reflect.Bool:
706 // Nothing to do
707 default:
708 return nil, fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, propStruct.Type().Field(i).Name, kind)
709 }
710 }
711
712 return values.Interface(), nil
713}
714
715func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
716 s := propertyValue.String()
717
718 count := strings.Count(s, "%")
719 if count == 0 {
720 return nil
721 }
722
723 if count > 1 {
724 return fmt.Errorf("value variable properties only support a single '%%'")
725 }
726
727 if !strings.Contains(s, "%s") {
728 return fmt.Errorf("unsupported %% in value variable property")
729 }
730
731 propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, configValue)))
732
Colin Cross9d34f352019-11-22 16:03:51 -0800733 return nil
734}
735
736func CanonicalizeToProperty(v string) string {
737 return strings.Map(func(r rune) rune {
738 switch {
739 case r >= 'A' && r <= 'Z',
740 r >= 'a' && r <= 'z',
741 r >= '0' && r <= '9',
742 r == '_':
743 return r
744 default:
745 return '_'
746 }
747 }, v)
748}
749
750func CanonicalizeToProperties(values []string) []string {
751 ret := make([]string, len(values))
752 for i, v := range values {
753 ret[i] = CanonicalizeToProperty(v)
754 }
755 return ret
756}
757
758type emptyInterfaceStruct struct {
759 i interface{}
760}
761
762var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type
Cole Faustb0a91332022-11-01 17:10:23 +0000763
764// InList checks if the string belongs to the list
765func InList(s string, list []string) bool {
766 for _, s2 := range list {
767 if s2 == s {
768 return true
769 }
770 }
771 return false
772}