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