1// 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/parser"
25	"github.com/google/blueprint/proptools"
26)
27
28const conditionsDefault = "conditions_default"
29
30var SoongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
31
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
113	// the list of boolean SOONG_CONFIG variables that this module type will read
114	Bool_variables []string
115
116	// 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
120	// 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
124	// 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
153	if mt, errs := newModuleType(props); len(errs) > 0 {
154		return errs
155	} else {
156		v.ModuleTypes[props.Name] = mt
157	}
158
159	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
182	vals := make(map[string]bool, len(stringProps.Values))
183	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)}
186		} else if _, ok := vals[name]; ok {
187			return []error{fmt.Errorf("soong_config_string_variable: values property error: duplicate value: %q", name)}
188		}
189		vals[name] = true
190	}
191
192	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:
247//
248//	*struct {
249//	    Soong_config_variables struct {
250//	        Board struct {
251//	            Soc_a interface{}
252//	            Soc_b interface{}
253//	        }
254//	    }
255//	}
256//
257// And whose value is:
258//
259//	&{
260//	    Soong_config_variables: {
261//	        Board: {
262//	            Soc_a: (*struct{ Cflags []string })(nil),
263//	            Soc_b: (*struct{ Cflags []string })(nil),
264//	        },
265//	    },
266//	}
267func CreateProperties(factoryProps []interface{}, moduleType *ModuleType) reflect.Value {
268	var fields []reflect.StructField
269
270	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{{
283		Name: SoongConfigProperty,
284		Type: reflect.StructOf(fields),
285	}})
286
287	props := reflect.New(typ)
288	structConditions := props.Elem().FieldByName(SoongConfigProperty)
289
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
307		// Iterate while the list is non-empty so it can be modified in the loop.
308		for len(affectableProperties) > 0 {
309			p := affectableProperties[0]
310			if !strings.HasPrefix(affectableProperties[0], prefix) {
311				// 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.
314				break
315			}
316
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
322				// 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.
326				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				}
344				// The first element in the list has been handled, remove it from the list.
345				affectableProperties = affectableProperties[1:]
346			}
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.
423// Expects that props contains a struct field with name soong_config_variables. The fields within
424// soong_config_variables are expected to be in the same order as moduleType.Variables.
425func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
426	var ret []interface{}
427	props = props.Elem().FieldByName(SoongConfigProperty)
428	for i, c := range moduleType.Variables {
429		if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
430			return nil, err
431		} else if ps != nil {
432			ret = append(ret, ps)
433		}
434	}
435	return ret, nil
436}
437
438type ModuleType struct {
439	BaseModuleType  string
440	ConfigNamespace string
441	Variables       []soongConfigVariable
442
443	affectableProperties []string
444	variableNames        []string
445}
446
447func 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
475	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
487	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
499type 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.
513	PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
514}
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
532	var values []string
533	values = append(values, s.values...)
534	values = append(values, conditionsDefault)
535	for _, v := range values {
536		fields = append(fields, reflect.StructField{
537			Name: proptools.FieldNameForProperty(v),
538			Type: emptyInterfaceType,
539		})
540	}
541
542	return reflect.StructOf(fields)
543}
544
545// initializeProperties initializes properties to zero value of typ for supported values and a final
546// conditions default field.
547func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
548	for i := range s.values {
549		v.Field(i).Set(reflect.Zero(typ))
550	}
551	v.Field(len(s.values)).Set(reflect.Zero(typ)) // conditions default is the final value
552}
553
554// 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.
556func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
557	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	}
561	for j, v := range s.values {
562		f := values.Field(j)
563		if configValue == v && !f.Elem().IsNil() {
564			return f.Interface(), nil
565		}
566	}
567	// 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
571}
572
573// Struct to allow conditions set based on a boolean variable
574type boolVariable struct {
575	baseVariable
576}
577
578// newBoolVariable constructs a boolVariable with the given name
579func newBoolVariable(name string) *boolVariable {
580	return &boolVariable{
581		baseVariable{
582			variable: name,
583		},
584	}
585}
586
587func (b boolVariable) variableValuesType() reflect.Type {
588	return emptyInterfaceType
589}
590
591// initializeProperties initializes a property to zero value of typ with an additional conditions
592// default field.
593func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
594	initializePropertiesWithDefault(v, typ)
595}
596
597// 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))
606	}
607
608	// 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	}
663	return nil, nil
664}
665
666// Struct to allow conditions set based on a value variable, supporting string substitution.
667type valueVariable struct {
668	baseVariable
669}
670
671func (s *valueVariable) variableValuesType() reflect.Type {
672	return emptyInterfaceType
673}
674
675// initializeProperties initializes a property to zero value of typ with an additional conditions
676// default field.
677func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
678	initializePropertiesWithDefault(v, typ)
679}
680
681// 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.
685func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
686	// If this variable was not referenced in the module, there are no properties to apply.
687	if !values.IsValid() || values.Elem().IsZero() {
688		return nil, nil
689	}
690	if !config.IsSet(s.variable) {
691		return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
692	}
693	configValue := config.String(s.variable)
694
695	values = removeDefault(values)
696	propStruct := values.Elem()
697	if !propStruct.IsValid() {
698		return nil, nil
699	}
700	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 {
708	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()
716			kind = field.Kind()
717		}
718		switch kind {
719		case reflect.String:
720			err := printfIntoProperty(field, configValue)
721			if err != nil {
722				fieldName = append(fieldName, propStruct.Type().Field(i).Name)
723				return fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, strings.Join(fieldName, "."), err)
724			}
725		case reflect.Slice:
726			for j := 0; j < field.Len(); j++ {
727				err := printfIntoProperty(field.Index(j), configValue)
728				if err != nil {
729					fieldName = append(fieldName, propStruct.Type().Field(i).Name)
730					return fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, strings.Join(fieldName, "."), err)
731				}
732			}
733		case reflect.Bool:
734			// Nothing to do
735		case reflect.Struct:
736			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]
747			}
748		default:
749			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)
751		}
752	}
753	return nil
754}
755
756// 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:
827			fieldName = append(fieldName, propStruct.Type().Field(i).Name)
828			if err := s.printfIntoPropertyRecursive(fieldName, field, configValues); err != nil {
829				return err
830			}
831			fieldName = fieldName[:len(fieldName)-1]
832		default:
833			fieldName = append(fieldName, propStruct.Type().Field(i).Name)
834			return fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, strings.Join(fieldName, "."), kind)
835		}
836	}
837	return nil
838}
839
840func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
841	s := propertyValue.String()
842
843	count := strings.Count(s, "%")
844	if count == 0 {
845		return nil
846	}
847
848	if count > 1 {
849		return fmt.Errorf("list/value variable properties only support a single '%%'")
850	}
851
852	if !strings.Contains(s, "%s") {
853		return fmt.Errorf("unsupported %% in value variable property")
854	}
855
856	propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, configValue)))
857
858	return nil
859}
860
861func CanonicalizeToProperty(v string) string {
862	return strings.Map(func(r rune) rune {
863		switch {
864		case r >= 'A' && r <= 'Z',
865			r >= 'a' && r <= 'z',
866			r >= '0' && r <= '9',
867			r == '_':
868			return r
869		default:
870			return '_'
871		}
872	}, v)
873}
874
875func CanonicalizeToProperties(values []string) []string {
876	ret := make([]string, len(values))
877	for i, v := range values {
878		ret[i] = CanonicalizeToProperty(v)
879	}
880	return ret
881}
882
883type emptyInterfaceStruct struct {
884	i interface{}
885}
886
887var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type
888
889// InList checks if the string belongs to the list
890func InList(s string, list []string) bool {
891	for _, s2 := range list {
892		if s2 == s {
893			return true
894		}
895	}
896	return false
897}
898