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