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"
25	"github.com/google/blueprint/parser"
26	"github.com/google/blueprint/proptools"
27)
28
29const conditionsDefault = "conditions_default"
30
31var 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
114	// the list of boolean SOONG_CONFIG variables that this module type will read
115	Bool_variables []string
116
117	// 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
121	// 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
150	if mt, errs := newModuleType(props); len(errs) > 0 {
151		return errs
152	} else {
153		v.ModuleTypes[props.Name] = mt
154	}
155
156	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
179	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
185	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
298		// Iterate while the list is non-empty so it can be modified in the loop.
299		for len(affectableProperties) > 0 {
300			p := affectableProperties[0]
301			if !strings.HasPrefix(affectableProperties[0], prefix) {
302				// 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.
305				break
306			}
307
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
313				// 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.
317				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				}
335				// The first element in the list has been handled, remove it from the list.
336				affectableProperties = affectableProperties[1:]
337			}
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.
414// Expects that props contains a struct field with name soong_config_variables. The fields within
415// soong_config_variables are expected to be in the same order as moduleType.Variables.
416func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
417	var ret []interface{}
418	props = props.Elem().FieldByName(soongConfigProperty)
419	for i, c := range moduleType.Variables {
420		if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
421			return nil, err
422		} else if ps != nil {
423			ret = append(ret, ps)
424		}
425	}
426	return ret, nil
427}
428
429type ModuleType struct {
430	BaseModuleType  string
431	ConfigNamespace string
432	Variables       []soongConfigVariable
433
434	affectableProperties []string
435	variableNames        []string
436}
437
438func 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
478type 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.
492	PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
493}
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
511	var values []string
512	values = append(values, s.values...)
513	values = append(values, conditionsDefault)
514	for _, v := range values {
515		fields = append(fields, reflect.StructField{
516			Name: proptools.FieldNameForProperty(v),
517			Type: emptyInterfaceType,
518		})
519	}
520
521	return reflect.StructOf(fields)
522}
523
524// initializeProperties initializes properties to zero value of typ for supported values and a final
525// conditions default field.
526func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
527	for i := range s.values {
528		v.Field(i).Set(reflect.Zero(typ))
529	}
530	v.Field(len(s.values)).Set(reflect.Zero(typ)) // conditions default is the final value
531}
532
533// 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.
535func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
536	for j, v := range s.values {
537		f := values.Field(j)
538		if config.String(s.variable) == v && !f.Elem().IsNil() {
539			return f.Interface(), nil
540		}
541	}
542	// 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
546}
547
548// Struct to allow conditions set based on a boolean variable
549type boolVariable struct {
550	baseVariable
551}
552
553// newBoolVariable constructs a boolVariable with the given name
554func newBoolVariable(name string) *boolVariable {
555	return &boolVariable{
556		baseVariable{
557			variable: name,
558		},
559	}
560}
561
562func (b boolVariable) variableValuesType() reflect.Type {
563	return emptyInterfaceType
564}
565
566// initializeProperties initializes a property to zero value of typ with an additional conditions
567// default field.
568func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
569	initializePropertiesWithDefault(v, typ)
570}
571
572// 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))
581	}
582
583	// 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	}
638	return nil, nil
639}
640
641// Struct to allow conditions set based on a value variable, supporting string substitution.
642type valueVariable struct {
643	baseVariable
644}
645
646func (s *valueVariable) variableValuesType() reflect.Type {
647	return emptyInterfaceType
648}
649
650// initializeProperties initializes a property to zero value of typ with an additional conditions
651// default field.
652func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
653	initializePropertiesWithDefault(v, typ)
654}
655
656// 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.
660func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
661	// If this variable was not referenced in the module, there are no properties to apply.
662	if !values.IsValid() || values.Elem().IsZero() {
663		return nil, nil
664	}
665	if !config.IsSet(s.variable) {
666		return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
667	}
668	configValue := config.String(s.variable)
669
670	values = removeDefault(values)
671	propStruct := values.Elem()
672	if !propStruct.IsValid() {
673		return nil, nil
674	}
675	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
725	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
755