• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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	"sync"
24
25	"github.com/google/blueprint/parser"
26	"github.com/google/blueprint/proptools"
27
28	"android/soong/starlark_fmt"
29)
30
31const conditionsDefault = "conditions_default"
32
33var SoongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
34
35// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file.  It caches the
36// result so each file is only parsed once.
37func Parse(r io.Reader, from string) (*SoongConfigDefinition, []error) {
38	scope := parser.NewScope(nil)
39	file, errs := parser.ParseAndEval(from, r, scope)
40
41	if len(errs) > 0 {
42		return nil, errs
43	}
44
45	mtDef := &SoongConfigDefinition{
46		ModuleTypes: make(map[string]*ModuleType),
47		variables:   make(map[string]soongConfigVariable),
48	}
49
50	for _, def := range file.Defs {
51		switch def := def.(type) {
52		case *parser.Module:
53			newErrs := processImportModuleDef(mtDef, def)
54
55			if len(newErrs) > 0 {
56				errs = append(errs, newErrs...)
57			}
58
59		case *parser.Assignment:
60			// Already handled via Scope object
61		default:
62			panic("unknown definition type")
63		}
64	}
65
66	if len(errs) > 0 {
67		return nil, errs
68	}
69
70	for name, moduleType := range mtDef.ModuleTypes {
71		for _, varName := range moduleType.variableNames {
72			if v, ok := mtDef.variables[varName]; ok {
73				moduleType.Variables = append(moduleType.Variables, v)
74			} else {
75				return nil, []error{
76					fmt.Errorf("unknown variable %q in module type %q", varName, name),
77				}
78			}
79		}
80	}
81
82	return mtDef, nil
83}
84
85func processImportModuleDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
86	switch def.Type {
87	case "soong_config_module_type":
88		return processModuleTypeDef(v, def)
89	case "soong_config_string_variable":
90		return processStringVariableDef(v, def)
91	case "soong_config_bool_variable":
92		return processBoolVariableDef(v, def)
93	default:
94		// Unknown module types will be handled when the file is parsed as a normal
95		// Android.bp file.
96	}
97
98	return nil
99}
100
101type ModuleTypeProperties struct {
102	// the name of the new module type.  Unlike most modules, this name does not need to be unique,
103	// although only one module type with any name will be importable into an Android.bp file.
104	Name string
105
106	// the module type that this module type will extend.
107	Module_type string
108
109	// the SOONG_CONFIG_NAMESPACE value from a BoardConfig.mk that this module type will read
110	// configuration variables from.
111	Config_namespace string
112
113	// the list of SOONG_CONFIG variables that this module type will read
114	Variables []string
115
116	// the list of boolean SOONG_CONFIG variables that this module type will read
117	Bool_variables []string
118
119	// the list of SOONG_CONFIG variables that this module type will read. The value will be
120	// inserted into the properties with %s substitution.
121	Value_variables []string
122
123	// the list of properties that this module type will extend.
124	Properties []string
125}
126
127func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
128
129	props := &ModuleTypeProperties{}
130
131	_, errs = proptools.UnpackProperties(def.Properties, props)
132	if len(errs) > 0 {
133		return errs
134	}
135
136	if props.Name == "" {
137		errs = append(errs, fmt.Errorf("name property must be set"))
138	}
139
140	if props.Config_namespace == "" {
141		errs = append(errs, fmt.Errorf("config_namespace property must be set"))
142	}
143
144	if props.Module_type == "" {
145		errs = append(errs, fmt.Errorf("module_type property must be set"))
146	}
147
148	if len(errs) > 0 {
149		return errs
150	}
151
152	if mt, errs := newModuleType(props); len(errs) > 0 {
153		return errs
154	} else {
155		v.ModuleTypes[props.Name] = mt
156	}
157
158	return nil
159}
160
161type VariableProperties struct {
162	Name string
163}
164
165type StringVariableProperties struct {
166	Values []string
167}
168
169func processStringVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
170	stringProps := &StringVariableProperties{}
171
172	base, errs := processVariableDef(def, stringProps)
173	if len(errs) > 0 {
174		return errs
175	}
176
177	if len(stringProps.Values) == 0 {
178		return []error{fmt.Errorf("values property must be set")}
179	}
180
181	vals := make(map[string]bool, len(stringProps.Values))
182	for _, name := range stringProps.Values {
183		if err := checkVariableName(name); err != nil {
184			return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
185		} else if _, ok := vals[name]; ok {
186			return []error{fmt.Errorf("soong_config_string_variable: values property error: duplicate value: %q", name)}
187		}
188		vals[name] = true
189	}
190
191	v.variables[base.variable] = &stringVariable{
192		baseVariable: base,
193		values:       CanonicalizeToProperties(stringProps.Values),
194	}
195
196	return nil
197}
198
199func processBoolVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
200	base, errs := processVariableDef(def)
201	if len(errs) > 0 {
202		return errs
203	}
204
205	v.variables[base.variable] = &boolVariable{
206		baseVariable: base,
207	}
208
209	return nil
210}
211
212func processVariableDef(def *parser.Module,
213	extraProps ...interface{}) (cond baseVariable, errs []error) {
214
215	props := &VariableProperties{}
216
217	allProps := append([]interface{}{props}, extraProps...)
218
219	_, errs = proptools.UnpackProperties(def.Properties, allProps...)
220	if len(errs) > 0 {
221		return baseVariable{}, errs
222	}
223
224	if props.Name == "" {
225		return baseVariable{}, []error{fmt.Errorf("name property must be set")}
226	}
227
228	return baseVariable{
229		variable: props.Name,
230	}, nil
231}
232
233type SoongConfigDefinition struct {
234	ModuleTypes map[string]*ModuleType
235
236	variables map[string]soongConfigVariable
237}
238
239// Bp2BuildSoongConfigDefinition keeps a global record of all soong config
240// string vars, bool vars and value vars created by every
241// soong_config_module_type in this build.
242type Bp2BuildSoongConfigDefinitions struct {
243	StringVars map[string]map[string]bool
244	BoolVars   map[string]bool
245	ValueVars  map[string]bool
246}
247
248var bp2buildSoongConfigVarsLock sync.Mutex
249
250// SoongConfigVariablesForBp2build extracts information from a
251// SoongConfigDefinition that bp2build needs to generate constraint settings and
252// values for, in order to migrate soong_config_module_type usages to Bazel.
253func (defs *Bp2BuildSoongConfigDefinitions) AddVars(mtDef *SoongConfigDefinition) {
254	// In bp2build mode, this method is called concurrently in goroutines from
255	// loadhooks while parsing soong_config_module_type, so add a mutex to
256	// prevent concurrent map writes. See b/207572723
257	bp2buildSoongConfigVarsLock.Lock()
258	defer bp2buildSoongConfigVarsLock.Unlock()
259
260	if defs.StringVars == nil {
261		defs.StringVars = make(map[string]map[string]bool)
262	}
263	if defs.BoolVars == nil {
264		defs.BoolVars = make(map[string]bool)
265	}
266	if defs.ValueVars == nil {
267		defs.ValueVars = make(map[string]bool)
268	}
269	// varCache contains a cache of string variables namespace + property
270	// The same variable may be used in multiple module types (for example, if need support
271	// for cc_default and java_default), only need to process once
272	varCache := map[string]bool{}
273
274	for _, moduleType := range mtDef.ModuleTypes {
275		for _, v := range moduleType.Variables {
276			key := strings.Join([]string{moduleType.ConfigNamespace, v.variableProperty()}, "__")
277
278			// The same variable may be used in multiple module types (for example, if need support
279			// for cc_default and java_default), only need to process once
280			if _, keyInCache := varCache[key]; keyInCache {
281				continue
282			} else {
283				varCache[key] = true
284			}
285
286			if strVar, ok := v.(*stringVariable); ok {
287				if _, ok := defs.StringVars[key]; !ok {
288					defs.StringVars[key] = make(map[string]bool, len(strVar.values))
289				}
290				for _, value := range strVar.values {
291					defs.StringVars[key][value] = true
292				}
293			} else if _, ok := v.(*boolVariable); ok {
294				defs.BoolVars[key] = true
295			} else if _, ok := v.(*valueVariable); ok {
296				defs.ValueVars[key] = true
297			} else {
298				panic(fmt.Errorf("Unsupported variable type: %+v", v))
299			}
300		}
301	}
302}
303
304// This is a copy of the one available in soong/android/util.go, but depending
305// on the android package causes a cyclic dependency. A refactoring here is to
306// extract common utils out from android/utils.go for other packages like this.
307func sortedStringKeys(m interface{}) []string {
308	v := reflect.ValueOf(m)
309	if v.Kind() != reflect.Map {
310		panic(fmt.Sprintf("%#v is not a map", m))
311	}
312	keys := v.MapKeys()
313	s := make([]string, 0, len(keys))
314	for _, key := range keys {
315		s = append(s, key.String())
316	}
317	sort.Strings(s)
318	return s
319}
320
321// String emits the Soong config variable definitions as Starlark dictionaries.
322func (defs Bp2BuildSoongConfigDefinitions) String() string {
323	ret := ""
324	ret += "soong_config_bool_variables = "
325	ret += starlark_fmt.PrintBoolDict(defs.BoolVars, 0)
326	ret += "\n\n"
327
328	ret += "soong_config_value_variables = "
329	ret += starlark_fmt.PrintBoolDict(defs.ValueVars, 0)
330	ret += "\n\n"
331
332	stringVars := make(map[string][]string, len(defs.StringVars))
333	for k, v := range defs.StringVars {
334		stringVars[k] = sortedStringKeys(v)
335	}
336
337	ret += "soong_config_string_variables = "
338	ret += starlark_fmt.PrintStringListDict(stringVars, 0)
339
340	return ret
341}
342
343// CreateProperties returns a reflect.Value of a newly constructed type that contains the desired
344// property layout for the Soong config variables, with each possible value an interface{} that
345// contains a nil pointer to another newly constructed type that contains the affectable properties.
346// The reflect.Value will be cloned for each call to the Soong config module type's factory method.
347//
348// For example, the acme_cc_defaults example above would
349// produce a reflect.Value whose type is:
350//
351//	*struct {
352//	    Soong_config_variables struct {
353//	        Board struct {
354//	            Soc_a interface{}
355//	            Soc_b interface{}
356//	        }
357//	    }
358//	}
359//
360// And whose value is:
361//
362//	&{
363//	    Soong_config_variables: {
364//	        Board: {
365//	            Soc_a: (*struct{ Cflags []string })(nil),
366//	            Soc_b: (*struct{ Cflags []string })(nil),
367//	        },
368//	    },
369//	}
370func CreateProperties(factoryProps []interface{}, moduleType *ModuleType) reflect.Value {
371	var fields []reflect.StructField
372
373	affectablePropertiesType := createAffectablePropertiesType(moduleType.affectableProperties, factoryProps)
374	if affectablePropertiesType == nil {
375		return reflect.Value{}
376	}
377
378	for _, c := range moduleType.Variables {
379		fields = append(fields, reflect.StructField{
380			Name: proptools.FieldNameForProperty(c.variableProperty()),
381			Type: c.variableValuesType(),
382		})
383	}
384
385	typ := reflect.StructOf([]reflect.StructField{{
386		Name: SoongConfigProperty,
387		Type: reflect.StructOf(fields),
388	}})
389
390	props := reflect.New(typ)
391	structConditions := props.Elem().FieldByName(SoongConfigProperty)
392
393	for i, c := range moduleType.Variables {
394		c.initializeProperties(structConditions.Field(i), affectablePropertiesType)
395	}
396
397	return props
398}
399
400// createAffectablePropertiesType creates a reflect.Type of a struct that has a field for each affectable property
401// that exists in factoryProps.
402func createAffectablePropertiesType(affectableProperties []string, factoryProps []interface{}) reflect.Type {
403	affectableProperties = append([]string(nil), affectableProperties...)
404	sort.Strings(affectableProperties)
405
406	var recurse func(prefix string, aps []string) ([]string, reflect.Type)
407	recurse = func(prefix string, aps []string) ([]string, reflect.Type) {
408		var fields []reflect.StructField
409
410		// Iterate while the list is non-empty so it can be modified in the loop.
411		for len(affectableProperties) > 0 {
412			p := affectableProperties[0]
413			if !strings.HasPrefix(affectableProperties[0], prefix) {
414				// The properties are sorted and recurse is always called with a prefix that matches
415				// the first property in the list, so if we've reached one that doesn't match the
416				// prefix we are done with this prefix.
417				break
418			}
419
420			nestedProperty := strings.TrimPrefix(p, prefix)
421			if i := strings.IndexRune(nestedProperty, '.'); i >= 0 {
422				var nestedType reflect.Type
423				nestedPrefix := nestedProperty[:i+1]
424
425				// Recurse to handle the properties with the found prefix.  This will return
426				// an updated affectableProperties with the handled entries removed from the front
427				// of the list, and the type that contains the handled entries.  The type may be
428				// nil if none of the entries matched factoryProps.
429				affectableProperties, nestedType = recurse(prefix+nestedPrefix, affectableProperties)
430
431				if nestedType != nil {
432					nestedFieldName := proptools.FieldNameForProperty(strings.TrimSuffix(nestedPrefix, "."))
433
434					fields = append(fields, reflect.StructField{
435						Name: nestedFieldName,
436						Type: nestedType,
437					})
438				}
439			} else {
440				typ := typeForPropertyFromPropertyStructs(factoryProps, p)
441				if typ != nil {
442					fields = append(fields, reflect.StructField{
443						Name: proptools.FieldNameForProperty(nestedProperty),
444						Type: typ,
445					})
446				}
447				// The first element in the list has been handled, remove it from the list.
448				affectableProperties = affectableProperties[1:]
449			}
450		}
451
452		var typ reflect.Type
453		if len(fields) > 0 {
454			typ = reflect.StructOf(fields)
455		}
456		return affectableProperties, typ
457	}
458
459	affectableProperties, typ := recurse("", affectableProperties)
460	if len(affectableProperties) > 0 {
461		panic(fmt.Errorf("didn't handle all affectable properties"))
462	}
463
464	if typ != nil {
465		return reflect.PtrTo(typ)
466	}
467
468	return nil
469}
470
471func typeForPropertyFromPropertyStructs(psList []interface{}, property string) reflect.Type {
472	for _, ps := range psList {
473		if typ := typeForPropertyFromPropertyStruct(ps, property); typ != nil {
474			return typ
475		}
476	}
477
478	return nil
479}
480
481func typeForPropertyFromPropertyStruct(ps interface{}, property string) reflect.Type {
482	v := reflect.ValueOf(ps)
483	for len(property) > 0 {
484		if !v.IsValid() {
485			return nil
486		}
487
488		if v.Kind() == reflect.Interface {
489			if v.IsNil() {
490				return nil
491			} else {
492				v = v.Elem()
493			}
494		}
495
496		if v.Kind() == reflect.Ptr {
497			if v.IsNil() {
498				v = reflect.Zero(v.Type().Elem())
499			} else {
500				v = v.Elem()
501			}
502		}
503
504		if v.Kind() != reflect.Struct {
505			return nil
506		}
507
508		if index := strings.IndexRune(property, '.'); index >= 0 {
509			prefix := property[:index]
510			property = property[index+1:]
511
512			v = v.FieldByName(proptools.FieldNameForProperty(prefix))
513		} else {
514			f := v.FieldByName(proptools.FieldNameForProperty(property))
515			if !f.IsValid() {
516				return nil
517			}
518			return f.Type()
519		}
520	}
521	return nil
522}
523
524// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
525// based on SoongConfig values.
526// Expects that props contains a struct field with name soong_config_variables. The fields within
527// soong_config_variables are expected to be in the same order as moduleType.Variables.
528func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
529	var ret []interface{}
530	props = props.Elem().FieldByName(SoongConfigProperty)
531	for i, c := range moduleType.Variables {
532		if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
533			return nil, err
534		} else if ps != nil {
535			ret = append(ret, ps)
536		}
537	}
538	return ret, nil
539}
540
541type ModuleType struct {
542	BaseModuleType  string
543	ConfigNamespace string
544	Variables       []soongConfigVariable
545
546	affectableProperties []string
547	variableNames        []string
548}
549
550func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
551	mt := &ModuleType{
552		affectableProperties: props.Properties,
553		ConfigNamespace:      props.Config_namespace,
554		BaseModuleType:       props.Module_type,
555		variableNames:        props.Variables,
556	}
557
558	for _, name := range props.Bool_variables {
559		if err := checkVariableName(name); err != nil {
560			return nil, []error{fmt.Errorf("bool_variables %s", err)}
561		}
562
563		mt.Variables = append(mt.Variables, newBoolVariable(name))
564	}
565
566	for _, name := range props.Value_variables {
567		if err := checkVariableName(name); err != nil {
568			return nil, []error{fmt.Errorf("value_variables %s", err)}
569		}
570
571		mt.Variables = append(mt.Variables, &valueVariable{
572			baseVariable: baseVariable{
573				variable: name,
574			},
575		})
576	}
577
578	return mt, nil
579}
580
581func checkVariableName(name string) error {
582	if name == "" {
583		return fmt.Errorf("name must not be blank")
584	} else if name == conditionsDefault {
585		return fmt.Errorf("%q is reserved", conditionsDefault)
586	}
587	return nil
588}
589
590type soongConfigVariable interface {
591	// variableProperty returns the name of the variable.
592	variableProperty() string
593
594	// conditionalValuesType returns a reflect.Type that contains an interface{} for each possible value.
595	variableValuesType() reflect.Type
596
597	// initializeProperties is passed a reflect.Value of the reflect.Type returned by conditionalValuesType and a
598	// reflect.Type of the affectable properties, and should initialize each interface{} in the reflect.Value with
599	// the zero value of the affectable properties type.
600	initializeProperties(v reflect.Value, typ reflect.Type)
601
602	// PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
603	// to the module.
604	PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
605}
606
607type baseVariable struct {
608	variable string
609}
610
611func (c *baseVariable) variableProperty() string {
612	return CanonicalizeToProperty(c.variable)
613}
614
615type stringVariable struct {
616	baseVariable
617	values []string
618}
619
620func (s *stringVariable) variableValuesType() reflect.Type {
621	var fields []reflect.StructField
622
623	var values []string
624	values = append(values, s.values...)
625	values = append(values, conditionsDefault)
626	for _, v := range values {
627		fields = append(fields, reflect.StructField{
628			Name: proptools.FieldNameForProperty(v),
629			Type: emptyInterfaceType,
630		})
631	}
632
633	return reflect.StructOf(fields)
634}
635
636// initializeProperties initializes properties to zero value of typ for supported values and a final
637// conditions default field.
638func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
639	for i := range s.values {
640		v.Field(i).Set(reflect.Zero(typ))
641	}
642	v.Field(len(s.values)).Set(reflect.Zero(typ)) // conditions default is the final value
643}
644
645// Extracts an interface from values containing the properties to apply based on config.
646// If config does not match a value with a non-nil property set, the default value will be returned.
647func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
648	configValue := config.String(s.variable)
649	if configValue != "" && !InList(configValue, s.values) {
650		return nil, fmt.Errorf("Soong config property %q must be one of %v, found %q", s.variable, s.values, configValue)
651	}
652	for j, v := range s.values {
653		f := values.Field(j)
654		if configValue == v && !f.Elem().IsNil() {
655			return f.Interface(), nil
656		}
657	}
658	// if we have reached this point, we have checked all valid values of string and either:
659	//   * the value was not set
660	//   * the value was set but that value was not specified in the Android.bp file
661	return values.Field(len(s.values)).Interface(), nil
662}
663
664// Struct to allow conditions set based on a boolean variable
665type boolVariable struct {
666	baseVariable
667}
668
669// newBoolVariable constructs a boolVariable with the given name
670func newBoolVariable(name string) *boolVariable {
671	return &boolVariable{
672		baseVariable{
673			variable: name,
674		},
675	}
676}
677
678func (b boolVariable) variableValuesType() reflect.Type {
679	return emptyInterfaceType
680}
681
682// initializeProperties initializes a property to zero value of typ with an additional conditions
683// default field.
684func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
685	initializePropertiesWithDefault(v, typ)
686}
687
688// initializePropertiesWithDefault, initialize with zero value,  v to contain a field for each field
689// in typ, with an additional field for defaults of type typ. This should be used to initialize
690// boolVariable, valueVariable, or any future implementations of soongConfigVariable which support
691// one variable and a default.
692func initializePropertiesWithDefault(v reflect.Value, typ reflect.Type) {
693	sTyp := typ.Elem()
694	var fields []reflect.StructField
695	for i := 0; i < sTyp.NumField(); i++ {
696		fields = append(fields, sTyp.Field(i))
697	}
698
699	// create conditions_default field
700	nestedFieldName := proptools.FieldNameForProperty(conditionsDefault)
701	fields = append(fields, reflect.StructField{
702		Name: nestedFieldName,
703		Type: typ,
704	})
705
706	newTyp := reflect.PtrTo(reflect.StructOf(fields))
707	v.Set(reflect.Zero(newTyp))
708}
709
710// conditionsDefaultField extracts the conditions_default field from v. This is always the final
711// field if initialized with initializePropertiesWithDefault.
712func conditionsDefaultField(v reflect.Value) reflect.Value {
713	return v.Field(v.NumField() - 1)
714}
715
716// removeDefault removes the conditions_default field from values while retaining values from all
717// other fields. This allows
718func removeDefault(values reflect.Value) reflect.Value {
719	v := values.Elem().Elem()
720	s := conditionsDefaultField(v)
721	// if conditions_default field was not set, there will be no issues extending properties.
722	if !s.IsValid() {
723		return v
724	}
725
726	// If conditions_default field was set, it has the correct type for our property. Create a new
727	// reflect.Value of the conditions_default type and copy all fields (except for
728	// conditions_default) based on values to the result.
729	res := reflect.New(s.Type().Elem())
730	for i := 0; i < res.Type().Elem().NumField(); i++ {
731		val := v.Field(i)
732		res.Elem().Field(i).Set(val)
733	}
734
735	return res
736}
737
738// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
739// the module. If the value was not set, conditions_default interface will be returned; otherwise,
740// the interface in values, without conditions_default will be returned.
741func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
742	// If this variable was not referenced in the module, there are no properties to apply.
743	if values.Elem().IsZero() {
744		return nil, nil
745	}
746	if config.Bool(b.variable) {
747		values = removeDefault(values)
748		return values.Interface(), nil
749	}
750	v := values.Elem().Elem()
751	if f := conditionsDefaultField(v); f.IsValid() {
752		return f.Interface(), nil
753	}
754	return nil, nil
755}
756
757// Struct to allow conditions set based on a value variable, supporting string substitution.
758type valueVariable struct {
759	baseVariable
760}
761
762func (s *valueVariable) variableValuesType() reflect.Type {
763	return emptyInterfaceType
764}
765
766// initializeProperties initializes a property to zero value of typ with an additional conditions
767// default field.
768func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
769	initializePropertiesWithDefault(v, typ)
770}
771
772// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
773// the module. If the variable was not set, conditions_default interface will be returned;
774// otherwise, the interface in values, without conditions_default will be returned with all
775// appropriate string substitutions based on variable being set.
776func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
777	// If this variable was not referenced in the module, there are no properties to apply.
778	if !values.IsValid() || values.Elem().IsZero() {
779		return nil, nil
780	}
781	if !config.IsSet(s.variable) {
782		return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
783	}
784	configValue := config.String(s.variable)
785
786	values = removeDefault(values)
787	propStruct := values.Elem()
788	if !propStruct.IsValid() {
789		return nil, nil
790	}
791	for i := 0; i < propStruct.NumField(); i++ {
792		field := propStruct.Field(i)
793		kind := field.Kind()
794		if kind == reflect.Ptr {
795			if field.IsNil() {
796				continue
797			}
798			field = field.Elem()
799		}
800		switch kind {
801		case reflect.String:
802			err := printfIntoProperty(field, configValue)
803			if err != nil {
804				return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
805			}
806		case reflect.Slice:
807			for j := 0; j < field.Len(); j++ {
808				err := printfIntoProperty(field.Index(j), configValue)
809				if err != nil {
810					return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
811				}
812			}
813		case reflect.Bool:
814			// Nothing to do
815		default:
816			return nil, fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, propStruct.Type().Field(i).Name, kind)
817		}
818	}
819
820	return values.Interface(), nil
821}
822
823func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
824	s := propertyValue.String()
825
826	count := strings.Count(s, "%")
827	if count == 0 {
828		return nil
829	}
830
831	if count > 1 {
832		return fmt.Errorf("value variable properties only support a single '%%'")
833	}
834
835	if !strings.Contains(s, "%s") {
836		return fmt.Errorf("unsupported %% in value variable property")
837	}
838
839	propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, configValue)))
840
841	return nil
842}
843
844func CanonicalizeToProperty(v string) string {
845	return strings.Map(func(r rune) rune {
846		switch {
847		case r >= 'A' && r <= 'Z',
848			r >= 'a' && r <= 'z',
849			r >= '0' && r <= '9',
850			r == '_':
851			return r
852		default:
853			return '_'
854		}
855	}, v)
856}
857
858func CanonicalizeToProperties(values []string) []string {
859	ret := make([]string, len(values))
860	for i, v := range values {
861		ret[i] = CanonicalizeToProperty(v)
862	}
863	return ret
864}
865
866type emptyInterfaceStruct struct {
867	i interface{}
868}
869
870var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type
871
872// InList checks if the string belongs to the list
873func InList(s string, list []string) bool {
874	for _, s2 := range list {
875		if s2 == s {
876			return true
877		}
878	}
879	return false
880}
881