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