• 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
24	"github.com/google/blueprint"
25	"github.com/google/blueprint/parser"
26	"github.com/google/blueprint/proptools"
27)
28
29var soongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
30
31// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file.  It caches the
32// result so each file is only parsed once.
33func Parse(r io.Reader, from string) (*SoongConfigDefinition, []error) {
34	scope := parser.NewScope(nil)
35	file, errs := parser.ParseAndEval(from, r, scope)
36
37	if len(errs) > 0 {
38		return nil, errs
39	}
40
41	mtDef := &SoongConfigDefinition{
42		ModuleTypes: make(map[string]*ModuleType),
43		variables:   make(map[string]soongConfigVariable),
44	}
45
46	for _, def := range file.Defs {
47		switch def := def.(type) {
48		case *parser.Module:
49			newErrs := processImportModuleDef(mtDef, def)
50
51			if len(newErrs) > 0 {
52				errs = append(errs, newErrs...)
53			}
54
55		case *parser.Assignment:
56			// Already handled via Scope object
57		default:
58			panic("unknown definition type")
59		}
60	}
61
62	if len(errs) > 0 {
63		return nil, errs
64	}
65
66	for name, moduleType := range mtDef.ModuleTypes {
67		for _, varName := range moduleType.variableNames {
68			if v, ok := mtDef.variables[varName]; ok {
69				moduleType.Variables = append(moduleType.Variables, v)
70			} else {
71				return nil, []error{
72					fmt.Errorf("unknown variable %q in module type %q", varName, name),
73				}
74			}
75		}
76	}
77
78	return mtDef, nil
79}
80
81func processImportModuleDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
82	switch def.Type {
83	case "soong_config_module_type":
84		return processModuleTypeDef(v, def)
85	case "soong_config_string_variable":
86		return processStringVariableDef(v, def)
87	case "soong_config_bool_variable":
88		return processBoolVariableDef(v, def)
89	default:
90		// Unknown module types will be handled when the file is parsed as a normal
91		// Android.bp file.
92	}
93
94	return nil
95}
96
97type ModuleTypeProperties struct {
98	// the name of the new module type.  Unlike most modules, this name does not need to be unique,
99	// although only one module type with any name will be importable into an Android.bp file.
100	Name string
101
102	// the module type that this module type will extend.
103	Module_type string
104
105	// the SOONG_CONFIG_NAMESPACE value from a BoardConfig.mk that this module type will read
106	// configuration variables from.
107	Config_namespace string
108
109	// the list of SOONG_CONFIG variables that this module type will read
110	Variables []string
111
112	// the list of boolean SOONG_CONFIG variables that this module type will read
113	Bool_variables []string
114
115	// the list of SOONG_CONFIG variables that this module type will read. The value will be
116	// inserted into the properties with %s substitution.
117	Value_variables []string
118
119	// the list of properties that this module type will extend.
120	Properties []string
121}
122
123func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
124
125	props := &ModuleTypeProperties{}
126
127	_, errs = proptools.UnpackProperties(def.Properties, props)
128	if len(errs) > 0 {
129		return errs
130	}
131
132	if props.Name == "" {
133		errs = append(errs, fmt.Errorf("name property must be set"))
134	}
135
136	if props.Config_namespace == "" {
137		errs = append(errs, fmt.Errorf("config_namespace property must be set"))
138	}
139
140	if props.Module_type == "" {
141		errs = append(errs, fmt.Errorf("module_type property must be set"))
142	}
143
144	if len(errs) > 0 {
145		return errs
146	}
147
148	mt := &ModuleType{
149		affectableProperties: props.Properties,
150		ConfigNamespace:      props.Config_namespace,
151		BaseModuleType:       props.Module_type,
152		variableNames:        props.Variables,
153	}
154	v.ModuleTypes[props.Name] = mt
155
156	for _, name := range props.Bool_variables {
157		if name == "" {
158			return []error{fmt.Errorf("bool_variable name must not be blank")}
159		}
160
161		mt.Variables = append(mt.Variables, &boolVariable{
162			baseVariable: baseVariable{
163				variable: name,
164			},
165		})
166	}
167
168	for _, name := range props.Value_variables {
169		if name == "" {
170			return []error{fmt.Errorf("value_variables entry must not be blank")}
171		}
172
173		mt.Variables = append(mt.Variables, &valueVariable{
174			baseVariable: baseVariable{
175				variable: name,
176			},
177		})
178	}
179
180	return nil
181}
182
183type VariableProperties struct {
184	Name string
185}
186
187type StringVariableProperties struct {
188	Values []string
189}
190
191func processStringVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
192	stringProps := &StringVariableProperties{}
193
194	base, errs := processVariableDef(def, stringProps)
195	if len(errs) > 0 {
196		return errs
197	}
198
199	if len(stringProps.Values) == 0 {
200		return []error{fmt.Errorf("values property must be set")}
201	}
202
203	v.variables[base.variable] = &stringVariable{
204		baseVariable: base,
205		values:       CanonicalizeToProperties(stringProps.Values),
206	}
207
208	return nil
209}
210
211func processBoolVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
212	base, errs := processVariableDef(def)
213	if len(errs) > 0 {
214		return errs
215	}
216
217	v.variables[base.variable] = &boolVariable{
218		baseVariable: base,
219	}
220
221	return nil
222}
223
224func processVariableDef(def *parser.Module,
225	extraProps ...interface{}) (cond baseVariable, errs []error) {
226
227	props := &VariableProperties{}
228
229	allProps := append([]interface{}{props}, extraProps...)
230
231	_, errs = proptools.UnpackProperties(def.Properties, allProps...)
232	if len(errs) > 0 {
233		return baseVariable{}, errs
234	}
235
236	if props.Name == "" {
237		return baseVariable{}, []error{fmt.Errorf("name property must be set")}
238	}
239
240	return baseVariable{
241		variable: props.Name,
242	}, nil
243}
244
245type SoongConfigDefinition struct {
246	ModuleTypes map[string]*ModuleType
247
248	variables map[string]soongConfigVariable
249}
250
251// CreateProperties returns a reflect.Value of a newly constructed type that contains the desired
252// property layout for the Soong config variables, with each possible value an interface{} that
253// contains a nil pointer to another newly constructed type that contains the affectable properties.
254// The reflect.Value will be cloned for each call to the Soong config module type's factory method.
255//
256// For example, the acme_cc_defaults example above would
257// produce a reflect.Value whose type is:
258// *struct {
259//     Soong_config_variables struct {
260//         Board struct {
261//             Soc_a interface{}
262//             Soc_b interface{}
263//         }
264//     }
265// }
266// And whose value is:
267// &{
268//     Soong_config_variables: {
269//         Board: {
270//             Soc_a: (*struct{ Cflags []string })(nil),
271//             Soc_b: (*struct{ Cflags []string })(nil),
272//         },
273//     },
274// }
275func CreateProperties(factory blueprint.ModuleFactory, moduleType *ModuleType) reflect.Value {
276	var fields []reflect.StructField
277
278	_, factoryProps := factory()
279	affectablePropertiesType := createAffectablePropertiesType(moduleType.affectableProperties, factoryProps)
280	if affectablePropertiesType == nil {
281		return reflect.Value{}
282	}
283
284	for _, c := range moduleType.Variables {
285		fields = append(fields, reflect.StructField{
286			Name: proptools.FieldNameForProperty(c.variableProperty()),
287			Type: c.variableValuesType(),
288		})
289	}
290
291	typ := reflect.StructOf([]reflect.StructField{{
292		Name: soongConfigProperty,
293		Type: reflect.StructOf(fields),
294	}})
295
296	props := reflect.New(typ)
297	structConditions := props.Elem().FieldByName(soongConfigProperty)
298
299	for i, c := range moduleType.Variables {
300		c.initializeProperties(structConditions.Field(i), affectablePropertiesType)
301	}
302
303	return props
304}
305
306// createAffectablePropertiesType creates a reflect.Type of a struct that has a field for each affectable property
307// that exists in factoryProps.
308func createAffectablePropertiesType(affectableProperties []string, factoryProps []interface{}) reflect.Type {
309	affectableProperties = append([]string(nil), affectableProperties...)
310	sort.Strings(affectableProperties)
311
312	var recurse func(prefix string, aps []string) ([]string, reflect.Type)
313	recurse = func(prefix string, aps []string) ([]string, reflect.Type) {
314		var fields []reflect.StructField
315
316		for len(affectableProperties) > 0 {
317			p := affectableProperties[0]
318			if !strings.HasPrefix(affectableProperties[0], prefix) {
319				break
320			}
321			affectableProperties = affectableProperties[1:]
322
323			nestedProperty := strings.TrimPrefix(p, prefix)
324			if i := strings.IndexRune(nestedProperty, '.'); i >= 0 {
325				var nestedType reflect.Type
326				nestedPrefix := nestedProperty[:i+1]
327
328				affectableProperties, nestedType = recurse(prefix+nestedPrefix, affectableProperties)
329
330				if nestedType != nil {
331					nestedFieldName := proptools.FieldNameForProperty(strings.TrimSuffix(nestedPrefix, "."))
332
333					fields = append(fields, reflect.StructField{
334						Name: nestedFieldName,
335						Type: nestedType,
336					})
337				}
338			} else {
339				typ := typeForPropertyFromPropertyStructs(factoryProps, p)
340				if typ != nil {
341					fields = append(fields, reflect.StructField{
342						Name: proptools.FieldNameForProperty(nestedProperty),
343						Type: typ,
344					})
345				}
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.
423func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
424	var ret []interface{}
425	props = props.Elem().FieldByName(soongConfigProperty)
426	for i, c := range moduleType.Variables {
427		if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
428			return nil, err
429		} else if ps != nil {
430			ret = append(ret, ps)
431		}
432	}
433	return ret, nil
434}
435
436type ModuleType struct {
437	BaseModuleType  string
438	ConfigNamespace string
439	Variables       []soongConfigVariable
440
441	affectableProperties []string
442	variableNames        []string
443}
444
445type soongConfigVariable interface {
446	// variableProperty returns the name of the variable.
447	variableProperty() string
448
449	// conditionalValuesType returns a reflect.Type that contains an interface{} for each possible value.
450	variableValuesType() reflect.Type
451
452	// initializeProperties is passed a reflect.Value of the reflect.Type returned by conditionalValuesType and a
453	// reflect.Type of the affectable properties, and should initialize each interface{} in the reflect.Value with
454	// the zero value of the affectable properties type.
455	initializeProperties(v reflect.Value, typ reflect.Type)
456
457	// PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
458	// to the module.
459	PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
460}
461
462type baseVariable struct {
463	variable string
464}
465
466func (c *baseVariable) variableProperty() string {
467	return CanonicalizeToProperty(c.variable)
468}
469
470type stringVariable struct {
471	baseVariable
472	values []string
473}
474
475func (s *stringVariable) variableValuesType() reflect.Type {
476	var fields []reflect.StructField
477
478	for _, v := range s.values {
479		fields = append(fields, reflect.StructField{
480			Name: proptools.FieldNameForProperty(v),
481			Type: emptyInterfaceType,
482		})
483	}
484
485	return reflect.StructOf(fields)
486}
487
488func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
489	for i := range s.values {
490		v.Field(i).Set(reflect.Zero(typ))
491	}
492}
493
494func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
495	for j, v := range s.values {
496		if config.String(s.variable) == v {
497			return values.Field(j).Interface(), nil
498		}
499	}
500
501	return nil, nil
502}
503
504type boolVariable struct {
505	baseVariable
506}
507
508func (b boolVariable) variableValuesType() reflect.Type {
509	return emptyInterfaceType
510}
511
512func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
513	v.Set(reflect.Zero(typ))
514}
515
516func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
517	if config.Bool(b.variable) {
518		return values.Interface(), nil
519	}
520
521	return nil, nil
522}
523
524type valueVariable struct {
525	baseVariable
526}
527
528func (s *valueVariable) variableValuesType() reflect.Type {
529	return emptyInterfaceType
530}
531
532func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
533	v.Set(reflect.Zero(typ))
534}
535
536func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
537	if !config.IsSet(s.variable) {
538		return nil, nil
539	}
540	configValue := config.String(s.variable)
541
542	propStruct := values.Elem().Elem()
543	for i := 0; i < propStruct.NumField(); i++ {
544		field := propStruct.Field(i)
545		kind := field.Kind()
546		if kind == reflect.Ptr {
547			if field.IsNil() {
548				continue
549			}
550			field = field.Elem()
551		}
552		switch kind {
553		case reflect.String:
554			err := printfIntoProperty(field, configValue)
555			if err != nil {
556				return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
557			}
558		case reflect.Slice:
559			for j := 0; j < field.Len(); j++ {
560				err := printfIntoProperty(field.Index(j), configValue)
561				if err != nil {
562					return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
563				}
564			}
565		case reflect.Bool:
566			// Nothing to do
567		default:
568			return nil, fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, propStruct.Type().Field(i).Name, kind)
569		}
570	}
571
572	return values.Interface(), nil
573}
574
575func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
576	s := propertyValue.String()
577
578	count := strings.Count(s, "%")
579	if count == 0 {
580		return nil
581	}
582
583	if count > 1 {
584		return fmt.Errorf("value variable properties only support a single '%%'")
585	}
586
587	if !strings.Contains(s, "%s") {
588		return fmt.Errorf("unsupported %% in value variable property")
589	}
590
591	propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, configValue)))
592
593	return nil
594}
595
596func CanonicalizeToProperty(v string) string {
597	return strings.Map(func(r rune) rune {
598		switch {
599		case r >= 'A' && r <= 'Z',
600			r >= 'a' && r <= 'z',
601			r >= '0' && r <= '9',
602			r == '_':
603			return r
604		default:
605			return '_'
606		}
607	}, v)
608}
609
610func CanonicalizeToProperties(values []string) []string {
611	ret := make([]string, len(values))
612	for i, v := range values {
613		ret[i] = CanonicalizeToProperty(v)
614	}
615	return ret
616}
617
618type emptyInterfaceStruct struct {
619	i interface{}
620}
621
622var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type
623