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