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