blob: 34b180d7d04a42e45aca4ba9cb22450630825192 [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"
23
24 "github.com/google/blueprint"
25 "github.com/google/blueprint/parser"
26 "github.com/google/blueprint/proptools"
27)
28
Liz Kammer432bd592020-12-16 12:42:02 -080029const conditionsDefault = "conditions_default"
30
Colin Cross9d34f352019-11-22 16:03:51 -080031var soongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
32
33// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
34// result so each file is only parsed once.
35func Parse(r io.Reader, from string) (*SoongConfigDefinition, []error) {
36 scope := parser.NewScope(nil)
37 file, errs := parser.ParseAndEval(from, r, scope)
38
39 if len(errs) > 0 {
40 return nil, errs
41 }
42
43 mtDef := &SoongConfigDefinition{
44 ModuleTypes: make(map[string]*ModuleType),
45 variables: make(map[string]soongConfigVariable),
46 }
47
48 for _, def := range file.Defs {
49 switch def := def.(type) {
50 case *parser.Module:
51 newErrs := processImportModuleDef(mtDef, def)
52
53 if len(newErrs) > 0 {
54 errs = append(errs, newErrs...)
55 }
56
57 case *parser.Assignment:
58 // Already handled via Scope object
59 default:
60 panic("unknown definition type")
61 }
62 }
63
64 if len(errs) > 0 {
65 return nil, errs
66 }
67
68 for name, moduleType := range mtDef.ModuleTypes {
69 for _, varName := range moduleType.variableNames {
70 if v, ok := mtDef.variables[varName]; ok {
71 moduleType.Variables = append(moduleType.Variables, v)
72 } else {
73 return nil, []error{
74 fmt.Errorf("unknown variable %q in module type %q", varName, name),
75 }
76 }
77 }
78 }
79
80 return mtDef, nil
81}
82
83func processImportModuleDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
84 switch def.Type {
85 case "soong_config_module_type":
86 return processModuleTypeDef(v, def)
87 case "soong_config_string_variable":
88 return processStringVariableDef(v, def)
89 case "soong_config_bool_variable":
90 return processBoolVariableDef(v, def)
91 default:
92 // Unknown module types will be handled when the file is parsed as a normal
93 // Android.bp file.
94 }
95
96 return nil
97}
98
99type ModuleTypeProperties struct {
100 // the name of the new module type. Unlike most modules, this name does not need to be unique,
101 // although only one module type with any name will be importable into an Android.bp file.
102 Name string
103
104 // the module type that this module type will extend.
105 Module_type string
106
107 // the SOONG_CONFIG_NAMESPACE value from a BoardConfig.mk that this module type will read
108 // configuration variables from.
109 Config_namespace string
110
111 // the list of SOONG_CONFIG variables that this module type will read
112 Variables []string
113
Dan Willemsen2b8b89c2020-03-23 19:39:34 -0700114 // the list of boolean SOONG_CONFIG variables that this module type will read
115 Bool_variables []string
116
Dan Willemsenb0935db2020-03-23 19:42:18 -0700117 // the list of SOONG_CONFIG variables that this module type will read. The value will be
118 // inserted into the properties with %s substitution.
119 Value_variables []string
120
Colin Cross9d34f352019-11-22 16:03:51 -0800121 // the list of properties that this module type will extend.
122 Properties []string
123}
124
125func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
126
127 props := &ModuleTypeProperties{}
128
129 _, errs = proptools.UnpackProperties(def.Properties, props)
130 if len(errs) > 0 {
131 return errs
132 }
133
134 if props.Name == "" {
135 errs = append(errs, fmt.Errorf("name property must be set"))
136 }
137
138 if props.Config_namespace == "" {
139 errs = append(errs, fmt.Errorf("config_namespace property must be set"))
140 }
141
142 if props.Module_type == "" {
143 errs = append(errs, fmt.Errorf("module_type property must be set"))
144 }
145
146 if len(errs) > 0 {
147 return errs
148 }
149
Liz Kammer432bd592020-12-16 12:42:02 -0800150 if mt, errs := newModuleType(props); len(errs) > 0 {
151 return errs
152 } else {
153 v.ModuleTypes[props.Name] = mt
Dan Willemsenb0935db2020-03-23 19:42:18 -0700154 }
155
Colin Cross9d34f352019-11-22 16:03:51 -0800156 return nil
157}
158
159type VariableProperties struct {
160 Name string
161}
162
163type StringVariableProperties struct {
164 Values []string
165}
166
167func processStringVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
168 stringProps := &StringVariableProperties{}
169
170 base, errs := processVariableDef(def, stringProps)
171 if len(errs) > 0 {
172 return errs
173 }
174
175 if len(stringProps.Values) == 0 {
176 return []error{fmt.Errorf("values property must be set")}
177 }
178
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)}
182 }
183 }
184
Colin Cross9d34f352019-11-22 16:03:51 -0800185 v.variables[base.variable] = &stringVariable{
186 baseVariable: base,
187 values: CanonicalizeToProperties(stringProps.Values),
188 }
189
190 return nil
191}
192
193func processBoolVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
194 base, errs := processVariableDef(def)
195 if len(errs) > 0 {
196 return errs
197 }
198
199 v.variables[base.variable] = &boolVariable{
200 baseVariable: base,
201 }
202
203 return nil
204}
205
206func processVariableDef(def *parser.Module,
207 extraProps ...interface{}) (cond baseVariable, errs []error) {
208
209 props := &VariableProperties{}
210
211 allProps := append([]interface{}{props}, extraProps...)
212
213 _, errs = proptools.UnpackProperties(def.Properties, allProps...)
214 if len(errs) > 0 {
215 return baseVariable{}, errs
216 }
217
218 if props.Name == "" {
219 return baseVariable{}, []error{fmt.Errorf("name property must be set")}
220 }
221
222 return baseVariable{
223 variable: props.Name,
224 }, nil
225}
226
227type SoongConfigDefinition struct {
228 ModuleTypes map[string]*ModuleType
229
230 variables map[string]soongConfigVariable
231}
232
233// CreateProperties returns a reflect.Value of a newly constructed type that contains the desired
234// property layout for the Soong config variables, with each possible value an interface{} that
235// contains a nil pointer to another newly constructed type that contains the affectable properties.
236// The reflect.Value will be cloned for each call to the Soong config module type's factory method.
237//
238// For example, the acme_cc_defaults example above would
239// produce a reflect.Value whose type is:
240// *struct {
241// Soong_config_variables struct {
242// Board struct {
243// Soc_a interface{}
244// Soc_b interface{}
245// }
246// }
247// }
248// And whose value is:
249// &{
250// Soong_config_variables: {
251// Board: {
252// Soc_a: (*struct{ Cflags []string })(nil),
253// Soc_b: (*struct{ Cflags []string })(nil),
254// },
255// },
256// }
257func CreateProperties(factory blueprint.ModuleFactory, moduleType *ModuleType) reflect.Value {
258 var fields []reflect.StructField
259
260 _, factoryProps := factory()
261 affectablePropertiesType := createAffectablePropertiesType(moduleType.affectableProperties, factoryProps)
262 if affectablePropertiesType == nil {
263 return reflect.Value{}
264 }
265
266 for _, c := range moduleType.Variables {
267 fields = append(fields, reflect.StructField{
268 Name: proptools.FieldNameForProperty(c.variableProperty()),
269 Type: c.variableValuesType(),
270 })
271 }
272
273 typ := reflect.StructOf([]reflect.StructField{{
274 Name: soongConfigProperty,
275 Type: reflect.StructOf(fields),
276 }})
277
278 props := reflect.New(typ)
279 structConditions := props.Elem().FieldByName(soongConfigProperty)
280
281 for i, c := range moduleType.Variables {
282 c.initializeProperties(structConditions.Field(i), affectablePropertiesType)
283 }
284
285 return props
286}
287
288// createAffectablePropertiesType creates a reflect.Type of a struct that has a field for each affectable property
289// that exists in factoryProps.
290func createAffectablePropertiesType(affectableProperties []string, factoryProps []interface{}) reflect.Type {
291 affectableProperties = append([]string(nil), affectableProperties...)
292 sort.Strings(affectableProperties)
293
294 var recurse func(prefix string, aps []string) ([]string, reflect.Type)
295 recurse = func(prefix string, aps []string) ([]string, reflect.Type) {
296 var fields []reflect.StructField
297
Colin Cross997f27a2021-03-05 17:25:41 -0800298 // Iterate while the list is non-empty so it can be modified in the loop.
Colin Cross9d34f352019-11-22 16:03:51 -0800299 for len(affectableProperties) > 0 {
300 p := affectableProperties[0]
301 if !strings.HasPrefix(affectableProperties[0], prefix) {
Colin Cross997f27a2021-03-05 17:25:41 -0800302 // The properties are sorted and recurse is always called with a prefix that matches
303 // the first property in the list, so if we've reached one that doesn't match the
304 // prefix we are done with this prefix.
Colin Cross9d34f352019-11-22 16:03:51 -0800305 break
306 }
Colin Cross9d34f352019-11-22 16:03:51 -0800307
308 nestedProperty := strings.TrimPrefix(p, prefix)
309 if i := strings.IndexRune(nestedProperty, '.'); i >= 0 {
310 var nestedType reflect.Type
311 nestedPrefix := nestedProperty[:i+1]
312
Colin Cross997f27a2021-03-05 17:25:41 -0800313 // Recurse to handle the properties with the found prefix. This will return
314 // an updated affectableProperties with the handled entries removed from the front
315 // of the list, and the type that contains the handled entries. The type may be
316 // nil if none of the entries matched factoryProps.
Colin Cross9d34f352019-11-22 16:03:51 -0800317 affectableProperties, nestedType = recurse(prefix+nestedPrefix, affectableProperties)
318
319 if nestedType != nil {
320 nestedFieldName := proptools.FieldNameForProperty(strings.TrimSuffix(nestedPrefix, "."))
321
322 fields = append(fields, reflect.StructField{
323 Name: nestedFieldName,
324 Type: nestedType,
325 })
326 }
327 } else {
328 typ := typeForPropertyFromPropertyStructs(factoryProps, p)
329 if typ != nil {
330 fields = append(fields, reflect.StructField{
331 Name: proptools.FieldNameForProperty(nestedProperty),
332 Type: typ,
333 })
334 }
Colin Cross997f27a2021-03-05 17:25:41 -0800335 // The first element in the list has been handled, remove it from the list.
336 affectableProperties = affectableProperties[1:]
Colin Cross9d34f352019-11-22 16:03:51 -0800337 }
338 }
339
340 var typ reflect.Type
341 if len(fields) > 0 {
342 typ = reflect.StructOf(fields)
343 }
344 return affectableProperties, typ
345 }
346
347 affectableProperties, typ := recurse("", affectableProperties)
348 if len(affectableProperties) > 0 {
349 panic(fmt.Errorf("didn't handle all affectable properties"))
350 }
351
352 if typ != nil {
353 return reflect.PtrTo(typ)
354 }
355
356 return nil
357}
358
359func typeForPropertyFromPropertyStructs(psList []interface{}, property string) reflect.Type {
360 for _, ps := range psList {
361 if typ := typeForPropertyFromPropertyStruct(ps, property); typ != nil {
362 return typ
363 }
364 }
365
366 return nil
367}
368
369func typeForPropertyFromPropertyStruct(ps interface{}, property string) reflect.Type {
370 v := reflect.ValueOf(ps)
371 for len(property) > 0 {
372 if !v.IsValid() {
373 return nil
374 }
375
376 if v.Kind() == reflect.Interface {
377 if v.IsNil() {
378 return nil
379 } else {
380 v = v.Elem()
381 }
382 }
383
384 if v.Kind() == reflect.Ptr {
385 if v.IsNil() {
386 v = reflect.Zero(v.Type().Elem())
387 } else {
388 v = v.Elem()
389 }
390 }
391
392 if v.Kind() != reflect.Struct {
393 return nil
394 }
395
396 if index := strings.IndexRune(property, '.'); index >= 0 {
397 prefix := property[:index]
398 property = property[index+1:]
399
400 v = v.FieldByName(proptools.FieldNameForProperty(prefix))
401 } else {
402 f := v.FieldByName(proptools.FieldNameForProperty(property))
403 if !f.IsValid() {
404 return nil
405 }
406 return f.Type()
407 }
408 }
409 return nil
410}
411
412// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
413// based on SoongConfig values.
Liz Kammerfe8853d2020-12-16 09:34:33 -0800414// Expects that props contains a struct field with name soong_config_variables. The fields within
Liz Kammer432bd592020-12-16 12:42:02 -0800415// soong_config_variables are expected to be in the same order as moduleType.Variables.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700416func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
Colin Cross9d34f352019-11-22 16:03:51 -0800417 var ret []interface{}
418 props = props.Elem().FieldByName(soongConfigProperty)
419 for i, c := range moduleType.Variables {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700420 if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
421 return nil, err
422 } else if ps != nil {
Colin Cross9d34f352019-11-22 16:03:51 -0800423 ret = append(ret, ps)
424 }
425 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700426 return ret, nil
Colin Cross9d34f352019-11-22 16:03:51 -0800427}
428
429type ModuleType struct {
430 BaseModuleType string
431 ConfigNamespace string
432 Variables []soongConfigVariable
433
434 affectableProperties []string
435 variableNames []string
436}
437
Liz Kammer432bd592020-12-16 12:42:02 -0800438func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
439 mt := &ModuleType{
440 affectableProperties: props.Properties,
441 ConfigNamespace: props.Config_namespace,
442 BaseModuleType: props.Module_type,
443 variableNames: props.Variables,
444 }
445
446 for _, name := range props.Bool_variables {
447 if err := checkVariableName(name); err != nil {
448 return nil, []error{fmt.Errorf("bool_variables %s", err)}
449 }
450
451 mt.Variables = append(mt.Variables, newBoolVariable(name))
452 }
453
454 for _, name := range props.Value_variables {
455 if err := checkVariableName(name); err != nil {
456 return nil, []error{fmt.Errorf("value_variables %s", err)}
457 }
458
459 mt.Variables = append(mt.Variables, &valueVariable{
460 baseVariable: baseVariable{
461 variable: name,
462 },
463 })
464 }
465
466 return mt, nil
467}
468
469func checkVariableName(name string) error {
470 if name == "" {
471 return fmt.Errorf("name must not be blank")
472 } else if name == conditionsDefault {
473 return fmt.Errorf("%q is reserved", conditionsDefault)
474 }
475 return nil
476}
477
Colin Cross9d34f352019-11-22 16:03:51 -0800478type soongConfigVariable interface {
479 // variableProperty returns the name of the variable.
480 variableProperty() string
481
482 // conditionalValuesType returns a reflect.Type that contains an interface{} for each possible value.
483 variableValuesType() reflect.Type
484
485 // initializeProperties is passed a reflect.Value of the reflect.Type returned by conditionalValuesType and a
486 // reflect.Type of the affectable properties, and should initialize each interface{} in the reflect.Value with
487 // the zero value of the affectable properties type.
488 initializeProperties(v reflect.Value, typ reflect.Type)
489
490 // PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
491 // to the module.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700492 PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
Colin Cross9d34f352019-11-22 16:03:51 -0800493}
494
495type baseVariable struct {
496 variable string
497}
498
499func (c *baseVariable) variableProperty() string {
500 return CanonicalizeToProperty(c.variable)
501}
502
503type stringVariable struct {
504 baseVariable
505 values []string
506}
507
508func (s *stringVariable) variableValuesType() reflect.Type {
509 var fields []reflect.StructField
510
Liz Kammer432bd592020-12-16 12:42:02 -0800511 var values []string
512 values = append(values, s.values...)
513 values = append(values, conditionsDefault)
514 for _, v := range values {
Colin Cross9d34f352019-11-22 16:03:51 -0800515 fields = append(fields, reflect.StructField{
516 Name: proptools.FieldNameForProperty(v),
517 Type: emptyInterfaceType,
518 })
519 }
520
521 return reflect.StructOf(fields)
522}
523
Liz Kammer432bd592020-12-16 12:42:02 -0800524// initializeProperties initializes properties to zero value of typ for supported values and a final
525// conditions default field.
Colin Cross9d34f352019-11-22 16:03:51 -0800526func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
527 for i := range s.values {
528 v.Field(i).Set(reflect.Zero(typ))
529 }
Liz Kammer432bd592020-12-16 12:42:02 -0800530 v.Field(len(s.values)).Set(reflect.Zero(typ)) // conditions default is the final value
Colin Cross9d34f352019-11-22 16:03:51 -0800531}
532
Liz Kammer432bd592020-12-16 12:42:02 -0800533// Extracts an interface from values containing the properties to apply based on config.
534// 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 -0700535func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Colin Cross9d34f352019-11-22 16:03:51 -0800536 for j, v := range s.values {
Liz Kammer432bd592020-12-16 12:42:02 -0800537 f := values.Field(j)
538 if config.String(s.variable) == v && !f.Elem().IsNil() {
539 return f.Interface(), nil
Colin Cross9d34f352019-11-22 16:03:51 -0800540 }
541 }
Liz Kammer432bd592020-12-16 12:42:02 -0800542 // if we have reached this point, we have checked all valid values of string and either:
543 // * the value was not set
544 // * the value was set but that value was not specified in the Android.bp file
545 return values.Field(len(s.values)).Interface(), nil
Colin Cross9d34f352019-11-22 16:03:51 -0800546}
547
Liz Kammer432bd592020-12-16 12:42:02 -0800548// Struct to allow conditions set based on a boolean variable
Colin Cross9d34f352019-11-22 16:03:51 -0800549type boolVariable struct {
550 baseVariable
551}
552
Liz Kammer432bd592020-12-16 12:42:02 -0800553// newBoolVariable constructs a boolVariable with the given name
Liz Kammerfe8853d2020-12-16 09:34:33 -0800554func newBoolVariable(name string) *boolVariable {
555 return &boolVariable{
556 baseVariable{
557 variable: name,
558 },
559 }
560}
561
Colin Cross9d34f352019-11-22 16:03:51 -0800562func (b boolVariable) variableValuesType() reflect.Type {
563 return emptyInterfaceType
564}
565
Liz Kammer432bd592020-12-16 12:42:02 -0800566// initializeProperties initializes a property to zero value of typ with an additional conditions
567// default field.
Colin Cross9d34f352019-11-22 16:03:51 -0800568func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
Liz Kammer432bd592020-12-16 12:42:02 -0800569 initializePropertiesWithDefault(v, typ)
Colin Cross9d34f352019-11-22 16:03:51 -0800570}
571
Liz Kammer432bd592020-12-16 12:42:02 -0800572// initializePropertiesWithDefault, initialize with zero value, v to contain a field for each field
573// in typ, with an additional field for defaults of type typ. This should be used to initialize
574// boolVariable, valueVariable, or any future implementations of soongConfigVariable which support
575// one variable and a default.
576func initializePropertiesWithDefault(v reflect.Value, typ reflect.Type) {
577 sTyp := typ.Elem()
578 var fields []reflect.StructField
579 for i := 0; i < sTyp.NumField(); i++ {
580 fields = append(fields, sTyp.Field(i))
Colin Cross9d34f352019-11-22 16:03:51 -0800581 }
582
Liz Kammer432bd592020-12-16 12:42:02 -0800583 // create conditions_default field
584 nestedFieldName := proptools.FieldNameForProperty(conditionsDefault)
585 fields = append(fields, reflect.StructField{
586 Name: nestedFieldName,
587 Type: typ,
588 })
589
590 newTyp := reflect.PtrTo(reflect.StructOf(fields))
591 v.Set(reflect.Zero(newTyp))
592}
593
594// conditionsDefaultField extracts the conditions_default field from v. This is always the final
595// field if initialized with initializePropertiesWithDefault.
596func conditionsDefaultField(v reflect.Value) reflect.Value {
597 return v.Field(v.NumField() - 1)
598}
599
600// removeDefault removes the conditions_default field from values while retaining values from all
601// other fields. This allows
602func removeDefault(values reflect.Value) reflect.Value {
603 v := values.Elem().Elem()
604 s := conditionsDefaultField(v)
605 // if conditions_default field was not set, there will be no issues extending properties.
606 if !s.IsValid() {
607 return v
608 }
609
610 // If conditions_default field was set, it has the correct type for our property. Create a new
611 // reflect.Value of the conditions_default type and copy all fields (except for
612 // conditions_default) based on values to the result.
613 res := reflect.New(s.Type().Elem())
614 for i := 0; i < res.Type().Elem().NumField(); i++ {
615 val := v.Field(i)
616 res.Elem().Field(i).Set(val)
617 }
618
619 return res
620}
621
622// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
623// the module. If the value was not set, conditions_default interface will be returned; otherwise,
624// the interface in values, without conditions_default will be returned.
625func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
626 // If this variable was not referenced in the module, there are no properties to apply.
627 if values.Elem().IsZero() {
628 return nil, nil
629 }
630 if config.Bool(b.variable) {
631 values = removeDefault(values)
632 return values.Interface(), nil
633 }
634 v := values.Elem().Elem()
635 if f := conditionsDefaultField(v); f.IsValid() {
636 return f.Interface(), nil
637 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700638 return nil, nil
639}
640
Liz Kammer432bd592020-12-16 12:42:02 -0800641// Struct to allow conditions set based on a value variable, supporting string substitution.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700642type valueVariable struct {
643 baseVariable
644}
645
646func (s *valueVariable) variableValuesType() reflect.Type {
647 return emptyInterfaceType
648}
649
Liz Kammer432bd592020-12-16 12:42:02 -0800650// initializeProperties initializes a property to zero value of typ with an additional conditions
651// default field.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700652func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
Liz Kammer432bd592020-12-16 12:42:02 -0800653 initializePropertiesWithDefault(v, typ)
Dan Willemsenb0935db2020-03-23 19:42:18 -0700654}
655
Liz Kammer432bd592020-12-16 12:42:02 -0800656// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
657// the module. If the variable was not set, conditions_default interface will be returned;
658// otherwise, the interface in values, without conditions_default will be returned with all
659// appropriate string substitutions based on variable being set.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700660func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Liz Kammer432bd592020-12-16 12:42:02 -0800661 // If this variable was not referenced in the module, there are no properties to apply.
662 if !values.IsValid() || values.Elem().IsZero() {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700663 return nil, nil
664 }
Liz Kammer432bd592020-12-16 12:42:02 -0800665 if !config.IsSet(s.variable) {
666 return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
667 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700668 configValue := config.String(s.variable)
669
Liz Kammer432bd592020-12-16 12:42:02 -0800670 values = removeDefault(values)
671 propStruct := values.Elem()
Liz Kammer40ddfaa2020-12-16 10:59:00 -0800672 if !propStruct.IsValid() {
673 return nil, nil
674 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700675 for i := 0; i < propStruct.NumField(); i++ {
676 field := propStruct.Field(i)
677 kind := field.Kind()
678 if kind == reflect.Ptr {
679 if field.IsNil() {
680 continue
681 }
682 field = field.Elem()
683 }
684 switch kind {
685 case reflect.String:
686 err := printfIntoProperty(field, configValue)
687 if err != nil {
688 return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
689 }
690 case reflect.Slice:
691 for j := 0; j < field.Len(); j++ {
692 err := printfIntoProperty(field.Index(j), configValue)
693 if err != nil {
694 return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
695 }
696 }
697 case reflect.Bool:
698 // Nothing to do
699 default:
700 return nil, fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, propStruct.Type().Field(i).Name, kind)
701 }
702 }
703
704 return values.Interface(), nil
705}
706
707func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
708 s := propertyValue.String()
709
710 count := strings.Count(s, "%")
711 if count == 0 {
712 return nil
713 }
714
715 if count > 1 {
716 return fmt.Errorf("value variable properties only support a single '%%'")
717 }
718
719 if !strings.Contains(s, "%s") {
720 return fmt.Errorf("unsupported %% in value variable property")
721 }
722
723 propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, configValue)))
724
Colin Cross9d34f352019-11-22 16:03:51 -0800725 return nil
726}
727
728func CanonicalizeToProperty(v string) string {
729 return strings.Map(func(r rune) rune {
730 switch {
731 case r >= 'A' && r <= 'Z',
732 r >= 'a' && r <= 'z',
733 r >= '0' && r <= '9',
734 r == '_':
735 return r
736 default:
737 return '_'
738 }
739 }, v)
740}
741
742func CanonicalizeToProperties(values []string) []string {
743 ret := make([]string, len(values))
744 for i, v := range values {
745 ret[i] = CanonicalizeToProperty(v)
746 }
747 return ret
748}
749
750type emptyInterfaceStruct struct {
751 i interface{}
752}
753
754var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type