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