1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2023 Google Inc. All rights reserved. 2*1fa6dee9SAndroid Build Coastguard Worker// 3*1fa6dee9SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*1fa6dee9SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*1fa6dee9SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*1fa6dee9SAndroid Build Coastguard Worker// 7*1fa6dee9SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*1fa6dee9SAndroid Build Coastguard Worker// 9*1fa6dee9SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*1fa6dee9SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*1fa6dee9SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*1fa6dee9SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*1fa6dee9SAndroid Build Coastguard Worker// limitations under the License. 14*1fa6dee9SAndroid Build Coastguard Workerpackage proptools 15*1fa6dee9SAndroid Build Coastguard Worker 16*1fa6dee9SAndroid Build Coastguard Workerimport ( 17*1fa6dee9SAndroid Build Coastguard Worker "fmt" 18*1fa6dee9SAndroid Build Coastguard Worker "reflect" 19*1fa6dee9SAndroid Build Coastguard Worker "slices" 20*1fa6dee9SAndroid Build Coastguard Worker "strconv" 21*1fa6dee9SAndroid Build Coastguard Worker "strings" 22*1fa6dee9SAndroid Build Coastguard Worker 23*1fa6dee9SAndroid Build Coastguard Worker "github.com/google/blueprint/optional" 24*1fa6dee9SAndroid Build Coastguard Worker "github.com/google/blueprint/parser" 25*1fa6dee9SAndroid Build Coastguard Worker) 26*1fa6dee9SAndroid Build Coastguard Worker 27*1fa6dee9SAndroid Build Coastguard Worker// ConfigurableOptional is the same as ShallowOptional, but we use this separate 28*1fa6dee9SAndroid Build Coastguard Worker// name to reserve the ability to switch to an alternative implementation later. 29*1fa6dee9SAndroid Build Coastguard Workertype ConfigurableOptional[T any] struct { 30*1fa6dee9SAndroid Build Coastguard Worker shallowOptional optional.ShallowOptional[T] 31*1fa6dee9SAndroid Build Coastguard Worker} 32*1fa6dee9SAndroid Build Coastguard Worker 33*1fa6dee9SAndroid Build Coastguard Worker// IsPresent returns true if the optional contains a value 34*1fa6dee9SAndroid Build Coastguard Workerfunc (o *ConfigurableOptional[T]) IsPresent() bool { 35*1fa6dee9SAndroid Build Coastguard Worker return o.shallowOptional.IsPresent() 36*1fa6dee9SAndroid Build Coastguard Worker} 37*1fa6dee9SAndroid Build Coastguard Worker 38*1fa6dee9SAndroid Build Coastguard Worker// IsEmpty returns true if the optional does not have a value 39*1fa6dee9SAndroid Build Coastguard Workerfunc (o *ConfigurableOptional[T]) IsEmpty() bool { 40*1fa6dee9SAndroid Build Coastguard Worker return o.shallowOptional.IsEmpty() 41*1fa6dee9SAndroid Build Coastguard Worker} 42*1fa6dee9SAndroid Build Coastguard Worker 43*1fa6dee9SAndroid Build Coastguard Worker// Get() returns the value inside the optional. It panics if IsEmpty() returns true 44*1fa6dee9SAndroid Build Coastguard Workerfunc (o *ConfigurableOptional[T]) Get() T { 45*1fa6dee9SAndroid Build Coastguard Worker return o.shallowOptional.Get() 46*1fa6dee9SAndroid Build Coastguard Worker} 47*1fa6dee9SAndroid Build Coastguard Worker 48*1fa6dee9SAndroid Build Coastguard Worker// GetOrDefault() returns the value inside the optional if IsPresent() returns true, 49*1fa6dee9SAndroid Build Coastguard Worker// or the provided value otherwise. 50*1fa6dee9SAndroid Build Coastguard Workerfunc (o *ConfigurableOptional[T]) GetOrDefault(other T) T { 51*1fa6dee9SAndroid Build Coastguard Worker return o.shallowOptional.GetOrDefault(other) 52*1fa6dee9SAndroid Build Coastguard Worker} 53*1fa6dee9SAndroid Build Coastguard Worker 54*1fa6dee9SAndroid Build Coastguard Workertype ConfigurableElements interface { 55*1fa6dee9SAndroid Build Coastguard Worker string | bool | []string 56*1fa6dee9SAndroid Build Coastguard Worker} 57*1fa6dee9SAndroid Build Coastguard Worker 58*1fa6dee9SAndroid Build Coastguard Workertype ConfigurableEvaluator interface { 59*1fa6dee9SAndroid Build Coastguard Worker EvaluateConfiguration(condition ConfigurableCondition, property string) ConfigurableValue 60*1fa6dee9SAndroid Build Coastguard Worker PropertyErrorf(property, fmt string, args ...interface{}) 61*1fa6dee9SAndroid Build Coastguard Worker} 62*1fa6dee9SAndroid Build Coastguard Worker 63*1fa6dee9SAndroid Build Coastguard Worker// configurableMarker is just so that reflection can check type of the first field of 64*1fa6dee9SAndroid Build Coastguard Worker// the struct to determine if it is a configurable struct. 65*1fa6dee9SAndroid Build Coastguard Workertype configurableMarker bool 66*1fa6dee9SAndroid Build Coastguard Worker 67*1fa6dee9SAndroid Build Coastguard Workervar configurableMarkerType reflect.Type = reflect.TypeOf((*configurableMarker)(nil)).Elem() 68*1fa6dee9SAndroid Build Coastguard Worker 69*1fa6dee9SAndroid Build Coastguard Worker// ConfigurableCondition represents a condition that is being selected on, like 70*1fa6dee9SAndroid Build Coastguard Worker// arch(), os(), soong_config_variable("namespace", "variable"), or other variables. 71*1fa6dee9SAndroid Build Coastguard Worker// It's represented generically as a function name + arguments in blueprint, soong 72*1fa6dee9SAndroid Build Coastguard Worker// interprets the function name and args into specific variable values. 73*1fa6dee9SAndroid Build Coastguard Worker// 74*1fa6dee9SAndroid Build Coastguard Worker// ConfigurableCondition is treated as an immutable object so that it may be shared 75*1fa6dee9SAndroid Build Coastguard Worker// between different configurable properties. 76*1fa6dee9SAndroid Build Coastguard Workertype ConfigurableCondition struct { 77*1fa6dee9SAndroid Build Coastguard Worker functionName string 78*1fa6dee9SAndroid Build Coastguard Worker args []string 79*1fa6dee9SAndroid Build Coastguard Worker} 80*1fa6dee9SAndroid Build Coastguard Worker 81*1fa6dee9SAndroid Build Coastguard Workerfunc NewConfigurableCondition(functionName string, args []string) ConfigurableCondition { 82*1fa6dee9SAndroid Build Coastguard Worker return ConfigurableCondition{ 83*1fa6dee9SAndroid Build Coastguard Worker functionName: functionName, 84*1fa6dee9SAndroid Build Coastguard Worker args: slices.Clone(args), 85*1fa6dee9SAndroid Build Coastguard Worker } 86*1fa6dee9SAndroid Build Coastguard Worker} 87*1fa6dee9SAndroid Build Coastguard Worker 88*1fa6dee9SAndroid Build Coastguard Workerfunc (c ConfigurableCondition) FunctionName() string { 89*1fa6dee9SAndroid Build Coastguard Worker return c.functionName 90*1fa6dee9SAndroid Build Coastguard Worker} 91*1fa6dee9SAndroid Build Coastguard Worker 92*1fa6dee9SAndroid Build Coastguard Workerfunc (c ConfigurableCondition) NumArgs() int { 93*1fa6dee9SAndroid Build Coastguard Worker return len(c.args) 94*1fa6dee9SAndroid Build Coastguard Worker} 95*1fa6dee9SAndroid Build Coastguard Worker 96*1fa6dee9SAndroid Build Coastguard Workerfunc (c ConfigurableCondition) Arg(i int) string { 97*1fa6dee9SAndroid Build Coastguard Worker return c.args[i] 98*1fa6dee9SAndroid Build Coastguard Worker} 99*1fa6dee9SAndroid Build Coastguard Worker 100*1fa6dee9SAndroid Build Coastguard Workerfunc (c *ConfigurableCondition) String() string { 101*1fa6dee9SAndroid Build Coastguard Worker var sb strings.Builder 102*1fa6dee9SAndroid Build Coastguard Worker sb.WriteString(c.functionName) 103*1fa6dee9SAndroid Build Coastguard Worker sb.WriteRune('(') 104*1fa6dee9SAndroid Build Coastguard Worker for i, arg := range c.args { 105*1fa6dee9SAndroid Build Coastguard Worker sb.WriteString(strconv.Quote(arg)) 106*1fa6dee9SAndroid Build Coastguard Worker if i < len(c.args)-1 { 107*1fa6dee9SAndroid Build Coastguard Worker sb.WriteString(", ") 108*1fa6dee9SAndroid Build Coastguard Worker } 109*1fa6dee9SAndroid Build Coastguard Worker } 110*1fa6dee9SAndroid Build Coastguard Worker sb.WriteRune(')') 111*1fa6dee9SAndroid Build Coastguard Worker return sb.String() 112*1fa6dee9SAndroid Build Coastguard Worker} 113*1fa6dee9SAndroid Build Coastguard Worker 114*1fa6dee9SAndroid Build Coastguard Workerfunc (c *ConfigurableCondition) toParserConfigurableCondition() parser.ConfigurableCondition { 115*1fa6dee9SAndroid Build Coastguard Worker var args []parser.String 116*1fa6dee9SAndroid Build Coastguard Worker for _, arg := range c.args { 117*1fa6dee9SAndroid Build Coastguard Worker args = append(args, parser.String{Value: arg}) 118*1fa6dee9SAndroid Build Coastguard Worker } 119*1fa6dee9SAndroid Build Coastguard Worker return parser.ConfigurableCondition{ 120*1fa6dee9SAndroid Build Coastguard Worker FunctionName: c.functionName, 121*1fa6dee9SAndroid Build Coastguard Worker Args: args, 122*1fa6dee9SAndroid Build Coastguard Worker } 123*1fa6dee9SAndroid Build Coastguard Worker} 124*1fa6dee9SAndroid Build Coastguard Worker 125*1fa6dee9SAndroid Build Coastguard Workertype configurableValueType int 126*1fa6dee9SAndroid Build Coastguard Worker 127*1fa6dee9SAndroid Build Coastguard Workerconst ( 128*1fa6dee9SAndroid Build Coastguard Worker configurableValueTypeString configurableValueType = iota 129*1fa6dee9SAndroid Build Coastguard Worker configurableValueTypeBool 130*1fa6dee9SAndroid Build Coastguard Worker configurableValueTypeUndefined 131*1fa6dee9SAndroid Build Coastguard Worker configurableValueTypeStringList 132*1fa6dee9SAndroid Build Coastguard Worker) 133*1fa6dee9SAndroid Build Coastguard Worker 134*1fa6dee9SAndroid Build Coastguard Workerfunc (v *configurableValueType) patternType() configurablePatternType { 135*1fa6dee9SAndroid Build Coastguard Worker switch *v { 136*1fa6dee9SAndroid Build Coastguard Worker case configurableValueTypeString: 137*1fa6dee9SAndroid Build Coastguard Worker return configurablePatternTypeString 138*1fa6dee9SAndroid Build Coastguard Worker case configurableValueTypeBool: 139*1fa6dee9SAndroid Build Coastguard Worker return configurablePatternTypeBool 140*1fa6dee9SAndroid Build Coastguard Worker case configurableValueTypeStringList: 141*1fa6dee9SAndroid Build Coastguard Worker return configurablePatternTypeStringList 142*1fa6dee9SAndroid Build Coastguard Worker default: 143*1fa6dee9SAndroid Build Coastguard Worker panic("unimplemented") 144*1fa6dee9SAndroid Build Coastguard Worker } 145*1fa6dee9SAndroid Build Coastguard Worker} 146*1fa6dee9SAndroid Build Coastguard Worker 147*1fa6dee9SAndroid Build Coastguard Workerfunc (v *configurableValueType) String() string { 148*1fa6dee9SAndroid Build Coastguard Worker switch *v { 149*1fa6dee9SAndroid Build Coastguard Worker case configurableValueTypeString: 150*1fa6dee9SAndroid Build Coastguard Worker return "string" 151*1fa6dee9SAndroid Build Coastguard Worker case configurableValueTypeBool: 152*1fa6dee9SAndroid Build Coastguard Worker return "bool" 153*1fa6dee9SAndroid Build Coastguard Worker case configurableValueTypeStringList: 154*1fa6dee9SAndroid Build Coastguard Worker return "string_list" 155*1fa6dee9SAndroid Build Coastguard Worker case configurableValueTypeUndefined: 156*1fa6dee9SAndroid Build Coastguard Worker return "undefined" 157*1fa6dee9SAndroid Build Coastguard Worker default: 158*1fa6dee9SAndroid Build Coastguard Worker panic("unimplemented") 159*1fa6dee9SAndroid Build Coastguard Worker } 160*1fa6dee9SAndroid Build Coastguard Worker} 161*1fa6dee9SAndroid Build Coastguard Worker 162*1fa6dee9SAndroid Build Coastguard Worker// ConfigurableValue represents the value of a certain condition being selected on. 163*1fa6dee9SAndroid Build Coastguard Worker// This type mostly exists to act as a sum type between string, bool, and undefined. 164*1fa6dee9SAndroid Build Coastguard Workertype ConfigurableValue struct { 165*1fa6dee9SAndroid Build Coastguard Worker typ configurableValueType 166*1fa6dee9SAndroid Build Coastguard Worker stringValue string 167*1fa6dee9SAndroid Build Coastguard Worker boolValue bool 168*1fa6dee9SAndroid Build Coastguard Worker stringListValue []string 169*1fa6dee9SAndroid Build Coastguard Worker} 170*1fa6dee9SAndroid Build Coastguard Worker 171*1fa6dee9SAndroid Build Coastguard Workerfunc (c *ConfigurableValue) toExpression() parser.Expression { 172*1fa6dee9SAndroid Build Coastguard Worker switch c.typ { 173*1fa6dee9SAndroid Build Coastguard Worker case configurableValueTypeBool: 174*1fa6dee9SAndroid Build Coastguard Worker return &parser.Bool{Value: c.boolValue} 175*1fa6dee9SAndroid Build Coastguard Worker case configurableValueTypeString: 176*1fa6dee9SAndroid Build Coastguard Worker return &parser.String{Value: c.stringValue} 177*1fa6dee9SAndroid Build Coastguard Worker case configurableValueTypeStringList: 178*1fa6dee9SAndroid Build Coastguard Worker result := &parser.List{} 179*1fa6dee9SAndroid Build Coastguard Worker for _, s := range c.stringListValue { 180*1fa6dee9SAndroid Build Coastguard Worker result.Values = append(result.Values, &parser.String{Value: s}) 181*1fa6dee9SAndroid Build Coastguard Worker } 182*1fa6dee9SAndroid Build Coastguard Worker return result 183*1fa6dee9SAndroid Build Coastguard Worker default: 184*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Sprintf("Unhandled configurableValueType: %s", c.typ.String())) 185*1fa6dee9SAndroid Build Coastguard Worker } 186*1fa6dee9SAndroid Build Coastguard Worker} 187*1fa6dee9SAndroid Build Coastguard Worker 188*1fa6dee9SAndroid Build Coastguard Workerfunc (c *ConfigurableValue) String() string { 189*1fa6dee9SAndroid Build Coastguard Worker switch c.typ { 190*1fa6dee9SAndroid Build Coastguard Worker case configurableValueTypeString: 191*1fa6dee9SAndroid Build Coastguard Worker return strconv.Quote(c.stringValue) 192*1fa6dee9SAndroid Build Coastguard Worker case configurableValueTypeBool: 193*1fa6dee9SAndroid Build Coastguard Worker if c.boolValue { 194*1fa6dee9SAndroid Build Coastguard Worker return "true" 195*1fa6dee9SAndroid Build Coastguard Worker } else { 196*1fa6dee9SAndroid Build Coastguard Worker return "false" 197*1fa6dee9SAndroid Build Coastguard Worker } 198*1fa6dee9SAndroid Build Coastguard Worker case configurableValueTypeUndefined: 199*1fa6dee9SAndroid Build Coastguard Worker return "undefined" 200*1fa6dee9SAndroid Build Coastguard Worker default: 201*1fa6dee9SAndroid Build Coastguard Worker panic("unimplemented") 202*1fa6dee9SAndroid Build Coastguard Worker } 203*1fa6dee9SAndroid Build Coastguard Worker} 204*1fa6dee9SAndroid Build Coastguard Worker 205*1fa6dee9SAndroid Build Coastguard Workerfunc ConfigurableValueString(s string) ConfigurableValue { 206*1fa6dee9SAndroid Build Coastguard Worker return ConfigurableValue{ 207*1fa6dee9SAndroid Build Coastguard Worker typ: configurableValueTypeString, 208*1fa6dee9SAndroid Build Coastguard Worker stringValue: s, 209*1fa6dee9SAndroid Build Coastguard Worker } 210*1fa6dee9SAndroid Build Coastguard Worker} 211*1fa6dee9SAndroid Build Coastguard Worker 212*1fa6dee9SAndroid Build Coastguard Workerfunc ConfigurableValueBool(b bool) ConfigurableValue { 213*1fa6dee9SAndroid Build Coastguard Worker return ConfigurableValue{ 214*1fa6dee9SAndroid Build Coastguard Worker typ: configurableValueTypeBool, 215*1fa6dee9SAndroid Build Coastguard Worker boolValue: b, 216*1fa6dee9SAndroid Build Coastguard Worker } 217*1fa6dee9SAndroid Build Coastguard Worker} 218*1fa6dee9SAndroid Build Coastguard Worker 219*1fa6dee9SAndroid Build Coastguard Workerfunc ConfigurableValueStringList(l []string) ConfigurableValue { 220*1fa6dee9SAndroid Build Coastguard Worker return ConfigurableValue{ 221*1fa6dee9SAndroid Build Coastguard Worker typ: configurableValueTypeStringList, 222*1fa6dee9SAndroid Build Coastguard Worker stringListValue: slices.Clone(l), 223*1fa6dee9SAndroid Build Coastguard Worker } 224*1fa6dee9SAndroid Build Coastguard Worker} 225*1fa6dee9SAndroid Build Coastguard Worker 226*1fa6dee9SAndroid Build Coastguard Workerfunc ConfigurableValueUndefined() ConfigurableValue { 227*1fa6dee9SAndroid Build Coastguard Worker return ConfigurableValue{ 228*1fa6dee9SAndroid Build Coastguard Worker typ: configurableValueTypeUndefined, 229*1fa6dee9SAndroid Build Coastguard Worker } 230*1fa6dee9SAndroid Build Coastguard Worker} 231*1fa6dee9SAndroid Build Coastguard Worker 232*1fa6dee9SAndroid Build Coastguard Workertype configurablePatternType int 233*1fa6dee9SAndroid Build Coastguard Worker 234*1fa6dee9SAndroid Build Coastguard Workerconst ( 235*1fa6dee9SAndroid Build Coastguard Worker configurablePatternTypeString configurablePatternType = iota 236*1fa6dee9SAndroid Build Coastguard Worker configurablePatternTypeBool 237*1fa6dee9SAndroid Build Coastguard Worker configurablePatternTypeStringList 238*1fa6dee9SAndroid Build Coastguard Worker configurablePatternTypeDefault 239*1fa6dee9SAndroid Build Coastguard Worker configurablePatternTypeAny 240*1fa6dee9SAndroid Build Coastguard Worker) 241*1fa6dee9SAndroid Build Coastguard Worker 242*1fa6dee9SAndroid Build Coastguard Workerfunc (v *configurablePatternType) String() string { 243*1fa6dee9SAndroid Build Coastguard Worker switch *v { 244*1fa6dee9SAndroid Build Coastguard Worker case configurablePatternTypeString: 245*1fa6dee9SAndroid Build Coastguard Worker return "string" 246*1fa6dee9SAndroid Build Coastguard Worker case configurablePatternTypeBool: 247*1fa6dee9SAndroid Build Coastguard Worker return "bool" 248*1fa6dee9SAndroid Build Coastguard Worker case configurablePatternTypeStringList: 249*1fa6dee9SAndroid Build Coastguard Worker return "string_list" 250*1fa6dee9SAndroid Build Coastguard Worker case configurablePatternTypeDefault: 251*1fa6dee9SAndroid Build Coastguard Worker return "default" 252*1fa6dee9SAndroid Build Coastguard Worker case configurablePatternTypeAny: 253*1fa6dee9SAndroid Build Coastguard Worker return "any" 254*1fa6dee9SAndroid Build Coastguard Worker default: 255*1fa6dee9SAndroid Build Coastguard Worker panic("unimplemented") 256*1fa6dee9SAndroid Build Coastguard Worker } 257*1fa6dee9SAndroid Build Coastguard Worker} 258*1fa6dee9SAndroid Build Coastguard Worker 259*1fa6dee9SAndroid Build Coastguard Worker// ConfigurablePattern represents a concrete value for a ConfigurableCase. 260*1fa6dee9SAndroid Build Coastguard Worker// Currently this just means the value of whatever variable is being looked 261*1fa6dee9SAndroid Build Coastguard Worker// up with the ConfigurableCase, but in the future it may be expanded to 262*1fa6dee9SAndroid Build Coastguard Worker// match multiple values (e.g. ranges of integers like 3..7). 263*1fa6dee9SAndroid Build Coastguard Worker// 264*1fa6dee9SAndroid Build Coastguard Worker// ConfigurablePattern can represent different types of values, like 265*1fa6dee9SAndroid Build Coastguard Worker// strings vs bools. 266*1fa6dee9SAndroid Build Coastguard Worker// 267*1fa6dee9SAndroid Build Coastguard Worker// ConfigurablePattern must be immutable so it can be shared between 268*1fa6dee9SAndroid Build Coastguard Worker// different configurable properties. 269*1fa6dee9SAndroid Build Coastguard Workertype ConfigurablePattern struct { 270*1fa6dee9SAndroid Build Coastguard Worker typ configurablePatternType 271*1fa6dee9SAndroid Build Coastguard Worker stringValue string 272*1fa6dee9SAndroid Build Coastguard Worker boolValue bool 273*1fa6dee9SAndroid Build Coastguard Worker binding string 274*1fa6dee9SAndroid Build Coastguard Worker} 275*1fa6dee9SAndroid Build Coastguard Worker 276*1fa6dee9SAndroid Build Coastguard Workerfunc (c ConfigurablePattern) toParserSelectPattern() parser.SelectPattern { 277*1fa6dee9SAndroid Build Coastguard Worker switch c.typ { 278*1fa6dee9SAndroid Build Coastguard Worker case configurablePatternTypeString: 279*1fa6dee9SAndroid Build Coastguard Worker return parser.SelectPattern{ 280*1fa6dee9SAndroid Build Coastguard Worker Value: &parser.String{Value: c.stringValue}, 281*1fa6dee9SAndroid Build Coastguard Worker Binding: parser.Variable{Name: c.binding}, 282*1fa6dee9SAndroid Build Coastguard Worker } 283*1fa6dee9SAndroid Build Coastguard Worker case configurablePatternTypeBool: 284*1fa6dee9SAndroid Build Coastguard Worker return parser.SelectPattern{ 285*1fa6dee9SAndroid Build Coastguard Worker Value: &parser.Bool{Value: c.boolValue}, 286*1fa6dee9SAndroid Build Coastguard Worker Binding: parser.Variable{Name: c.binding}, 287*1fa6dee9SAndroid Build Coastguard Worker } 288*1fa6dee9SAndroid Build Coastguard Worker case configurablePatternTypeDefault: 289*1fa6dee9SAndroid Build Coastguard Worker return parser.SelectPattern{ 290*1fa6dee9SAndroid Build Coastguard Worker Value: &parser.String{Value: "__soong_conditions_default__"}, 291*1fa6dee9SAndroid Build Coastguard Worker Binding: parser.Variable{Name: c.binding}, 292*1fa6dee9SAndroid Build Coastguard Worker } 293*1fa6dee9SAndroid Build Coastguard Worker case configurablePatternTypeAny: 294*1fa6dee9SAndroid Build Coastguard Worker return parser.SelectPattern{ 295*1fa6dee9SAndroid Build Coastguard Worker Value: &parser.String{Value: "__soong_conditions_any__"}, 296*1fa6dee9SAndroid Build Coastguard Worker Binding: parser.Variable{Name: c.binding}, 297*1fa6dee9SAndroid Build Coastguard Worker } 298*1fa6dee9SAndroid Build Coastguard Worker default: 299*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Sprintf("unknown type %d", c.typ)) 300*1fa6dee9SAndroid Build Coastguard Worker } 301*1fa6dee9SAndroid Build Coastguard Worker} 302*1fa6dee9SAndroid Build Coastguard Worker 303*1fa6dee9SAndroid Build Coastguard Workerfunc NewStringConfigurablePattern(s string) ConfigurablePattern { 304*1fa6dee9SAndroid Build Coastguard Worker return ConfigurablePattern{ 305*1fa6dee9SAndroid Build Coastguard Worker typ: configurablePatternTypeString, 306*1fa6dee9SAndroid Build Coastguard Worker stringValue: s, 307*1fa6dee9SAndroid Build Coastguard Worker } 308*1fa6dee9SAndroid Build Coastguard Worker} 309*1fa6dee9SAndroid Build Coastguard Worker 310*1fa6dee9SAndroid Build Coastguard Workerfunc NewBoolConfigurablePattern(b bool) ConfigurablePattern { 311*1fa6dee9SAndroid Build Coastguard Worker return ConfigurablePattern{ 312*1fa6dee9SAndroid Build Coastguard Worker typ: configurablePatternTypeBool, 313*1fa6dee9SAndroid Build Coastguard Worker boolValue: b, 314*1fa6dee9SAndroid Build Coastguard Worker } 315*1fa6dee9SAndroid Build Coastguard Worker} 316*1fa6dee9SAndroid Build Coastguard Worker 317*1fa6dee9SAndroid Build Coastguard Workerfunc NewDefaultConfigurablePattern() ConfigurablePattern { 318*1fa6dee9SAndroid Build Coastguard Worker return ConfigurablePattern{ 319*1fa6dee9SAndroid Build Coastguard Worker typ: configurablePatternTypeDefault, 320*1fa6dee9SAndroid Build Coastguard Worker } 321*1fa6dee9SAndroid Build Coastguard Worker} 322*1fa6dee9SAndroid Build Coastguard Worker 323*1fa6dee9SAndroid Build Coastguard Workerfunc (p *ConfigurablePattern) matchesValue(v ConfigurableValue) bool { 324*1fa6dee9SAndroid Build Coastguard Worker if p.typ == configurablePatternTypeDefault { 325*1fa6dee9SAndroid Build Coastguard Worker return true 326*1fa6dee9SAndroid Build Coastguard Worker } 327*1fa6dee9SAndroid Build Coastguard Worker if v.typ == configurableValueTypeUndefined { 328*1fa6dee9SAndroid Build Coastguard Worker return false 329*1fa6dee9SAndroid Build Coastguard Worker } 330*1fa6dee9SAndroid Build Coastguard Worker if p.typ == configurablePatternTypeAny { 331*1fa6dee9SAndroid Build Coastguard Worker return true 332*1fa6dee9SAndroid Build Coastguard Worker } 333*1fa6dee9SAndroid Build Coastguard Worker if p.typ != v.typ.patternType() { 334*1fa6dee9SAndroid Build Coastguard Worker return false 335*1fa6dee9SAndroid Build Coastguard Worker } 336*1fa6dee9SAndroid Build Coastguard Worker switch p.typ { 337*1fa6dee9SAndroid Build Coastguard Worker case configurablePatternTypeString: 338*1fa6dee9SAndroid Build Coastguard Worker return p.stringValue == v.stringValue 339*1fa6dee9SAndroid Build Coastguard Worker case configurablePatternTypeBool: 340*1fa6dee9SAndroid Build Coastguard Worker return p.boolValue == v.boolValue 341*1fa6dee9SAndroid Build Coastguard Worker default: 342*1fa6dee9SAndroid Build Coastguard Worker panic("unimplemented") 343*1fa6dee9SAndroid Build Coastguard Worker } 344*1fa6dee9SAndroid Build Coastguard Worker} 345*1fa6dee9SAndroid Build Coastguard Worker 346*1fa6dee9SAndroid Build Coastguard Workerfunc (p *ConfigurablePattern) matchesValueType(v ConfigurableValue) bool { 347*1fa6dee9SAndroid Build Coastguard Worker if p.typ == configurablePatternTypeDefault { 348*1fa6dee9SAndroid Build Coastguard Worker return true 349*1fa6dee9SAndroid Build Coastguard Worker } 350*1fa6dee9SAndroid Build Coastguard Worker if v.typ == configurableValueTypeUndefined { 351*1fa6dee9SAndroid Build Coastguard Worker return true 352*1fa6dee9SAndroid Build Coastguard Worker } 353*1fa6dee9SAndroid Build Coastguard Worker if p.typ == configurablePatternTypeAny { 354*1fa6dee9SAndroid Build Coastguard Worker return true 355*1fa6dee9SAndroid Build Coastguard Worker } 356*1fa6dee9SAndroid Build Coastguard Worker return p.typ == v.typ.patternType() 357*1fa6dee9SAndroid Build Coastguard Worker} 358*1fa6dee9SAndroid Build Coastguard Worker 359*1fa6dee9SAndroid Build Coastguard Worker// ConfigurableCase represents a set of ConfigurablePatterns 360*1fa6dee9SAndroid Build Coastguard Worker// (exactly 1 pattern per ConfigurableCase), and a value to use 361*1fa6dee9SAndroid Build Coastguard Worker// if all of the patterns are matched. 362*1fa6dee9SAndroid Build Coastguard Worker// 363*1fa6dee9SAndroid Build Coastguard Worker// ConfigurableCase must be immutable so it can be shared between 364*1fa6dee9SAndroid Build Coastguard Worker// different configurable properties. 365*1fa6dee9SAndroid Build Coastguard Workertype ConfigurableCase[T ConfigurableElements] struct { 366*1fa6dee9SAndroid Build Coastguard Worker patterns []ConfigurablePattern 367*1fa6dee9SAndroid Build Coastguard Worker value parser.Expression 368*1fa6dee9SAndroid Build Coastguard Worker} 369*1fa6dee9SAndroid Build Coastguard Worker 370*1fa6dee9SAndroid Build Coastguard Workerfunc (c *ConfigurableCase[T]) toParserConfigurableCase() *parser.SelectCase { 371*1fa6dee9SAndroid Build Coastguard Worker var patterns []parser.SelectPattern 372*1fa6dee9SAndroid Build Coastguard Worker for _, p := range c.patterns { 373*1fa6dee9SAndroid Build Coastguard Worker patterns = append(patterns, p.toParserSelectPattern()) 374*1fa6dee9SAndroid Build Coastguard Worker } 375*1fa6dee9SAndroid Build Coastguard Worker return &parser.SelectCase{ 376*1fa6dee9SAndroid Build Coastguard Worker Patterns: patterns, 377*1fa6dee9SAndroid Build Coastguard Worker Value: c.value, 378*1fa6dee9SAndroid Build Coastguard Worker } 379*1fa6dee9SAndroid Build Coastguard Worker} 380*1fa6dee9SAndroid Build Coastguard Worker 381*1fa6dee9SAndroid Build Coastguard Workertype configurableCaseReflection interface { 382*1fa6dee9SAndroid Build Coastguard Worker initialize(patterns []ConfigurablePattern, value parser.Expression) 383*1fa6dee9SAndroid Build Coastguard Worker} 384*1fa6dee9SAndroid Build Coastguard Worker 385*1fa6dee9SAndroid Build Coastguard Workervar _ configurableCaseReflection = &ConfigurableCase[string]{} 386*1fa6dee9SAndroid Build Coastguard Worker 387*1fa6dee9SAndroid Build Coastguard Workerfunc NewConfigurableCase[T ConfigurableElements](patterns []ConfigurablePattern, value *T) ConfigurableCase[T] { 388*1fa6dee9SAndroid Build Coastguard Worker var valueExpr parser.Expression 389*1fa6dee9SAndroid Build Coastguard Worker if value == nil { 390*1fa6dee9SAndroid Build Coastguard Worker valueExpr = &parser.UnsetProperty{} 391*1fa6dee9SAndroid Build Coastguard Worker } else { 392*1fa6dee9SAndroid Build Coastguard Worker switch v := any(value).(type) { 393*1fa6dee9SAndroid Build Coastguard Worker case *string: 394*1fa6dee9SAndroid Build Coastguard Worker valueExpr = &parser.String{Value: *v} 395*1fa6dee9SAndroid Build Coastguard Worker case *bool: 396*1fa6dee9SAndroid Build Coastguard Worker valueExpr = &parser.Bool{Value: *v} 397*1fa6dee9SAndroid Build Coastguard Worker case *[]string: 398*1fa6dee9SAndroid Build Coastguard Worker innerValues := make([]parser.Expression, 0, len(*v)) 399*1fa6dee9SAndroid Build Coastguard Worker for _, x := range *v { 400*1fa6dee9SAndroid Build Coastguard Worker innerValues = append(innerValues, &parser.String{Value: x}) 401*1fa6dee9SAndroid Build Coastguard Worker } 402*1fa6dee9SAndroid Build Coastguard Worker valueExpr = &parser.List{Values: innerValues} 403*1fa6dee9SAndroid Build Coastguard Worker default: 404*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Sprintf("should be unreachable due to the ConfigurableElements restriction: %#v", value)) 405*1fa6dee9SAndroid Build Coastguard Worker } 406*1fa6dee9SAndroid Build Coastguard Worker } 407*1fa6dee9SAndroid Build Coastguard Worker // Clone the values so they can't be modified from soong 408*1fa6dee9SAndroid Build Coastguard Worker patterns = slices.Clone(patterns) 409*1fa6dee9SAndroid Build Coastguard Worker return ConfigurableCase[T]{ 410*1fa6dee9SAndroid Build Coastguard Worker patterns: patterns, 411*1fa6dee9SAndroid Build Coastguard Worker value: valueExpr, 412*1fa6dee9SAndroid Build Coastguard Worker } 413*1fa6dee9SAndroid Build Coastguard Worker} 414*1fa6dee9SAndroid Build Coastguard Worker 415*1fa6dee9SAndroid Build Coastguard Workerfunc (c *ConfigurableCase[T]) initialize(patterns []ConfigurablePattern, value parser.Expression) { 416*1fa6dee9SAndroid Build Coastguard Worker c.patterns = patterns 417*1fa6dee9SAndroid Build Coastguard Worker c.value = value 418*1fa6dee9SAndroid Build Coastguard Worker} 419*1fa6dee9SAndroid Build Coastguard Worker 420*1fa6dee9SAndroid Build Coastguard Worker// for the given T, return the reflect.type of configurableCase[T] 421*1fa6dee9SAndroid Build Coastguard Workerfunc configurableCaseType(configuredType reflect.Type) reflect.Type { 422*1fa6dee9SAndroid Build Coastguard Worker // I don't think it's possible to do this generically with go's 423*1fa6dee9SAndroid Build Coastguard Worker // current reflection apis unfortunately 424*1fa6dee9SAndroid Build Coastguard Worker switch configuredType.Kind() { 425*1fa6dee9SAndroid Build Coastguard Worker case reflect.String: 426*1fa6dee9SAndroid Build Coastguard Worker return reflect.TypeOf(ConfigurableCase[string]{}) 427*1fa6dee9SAndroid Build Coastguard Worker case reflect.Bool: 428*1fa6dee9SAndroid Build Coastguard Worker return reflect.TypeOf(ConfigurableCase[bool]{}) 429*1fa6dee9SAndroid Build Coastguard Worker case reflect.Slice: 430*1fa6dee9SAndroid Build Coastguard Worker switch configuredType.Elem().Kind() { 431*1fa6dee9SAndroid Build Coastguard Worker case reflect.String: 432*1fa6dee9SAndroid Build Coastguard Worker return reflect.TypeOf(ConfigurableCase[[]string]{}) 433*1fa6dee9SAndroid Build Coastguard Worker } 434*1fa6dee9SAndroid Build Coastguard Worker } 435*1fa6dee9SAndroid Build Coastguard Worker panic("unimplemented") 436*1fa6dee9SAndroid Build Coastguard Worker} 437*1fa6dee9SAndroid Build Coastguard Worker 438*1fa6dee9SAndroid Build Coastguard Worker// for the given T, return the reflect.type of Configurable[T] 439*1fa6dee9SAndroid Build Coastguard Workerfunc configurableType(configuredType reflect.Type) (reflect.Type, error) { 440*1fa6dee9SAndroid Build Coastguard Worker // I don't think it's possible to do this generically with go's 441*1fa6dee9SAndroid Build Coastguard Worker // current reflection apis unfortunately 442*1fa6dee9SAndroid Build Coastguard Worker switch configuredType.Kind() { 443*1fa6dee9SAndroid Build Coastguard Worker case reflect.String: 444*1fa6dee9SAndroid Build Coastguard Worker return reflect.TypeOf(Configurable[string]{}), nil 445*1fa6dee9SAndroid Build Coastguard Worker case reflect.Bool: 446*1fa6dee9SAndroid Build Coastguard Worker return reflect.TypeOf(Configurable[bool]{}), nil 447*1fa6dee9SAndroid Build Coastguard Worker case reflect.Slice: 448*1fa6dee9SAndroid Build Coastguard Worker switch configuredType.Elem().Kind() { 449*1fa6dee9SAndroid Build Coastguard Worker case reflect.String: 450*1fa6dee9SAndroid Build Coastguard Worker return reflect.TypeOf(Configurable[[]string]{}), nil 451*1fa6dee9SAndroid Build Coastguard Worker } 452*1fa6dee9SAndroid Build Coastguard Worker } 453*1fa6dee9SAndroid Build Coastguard Worker return nil, fmt.Errorf("configurable structs can only contain strings, bools, or string slices, found %s", configuredType.String()) 454*1fa6dee9SAndroid Build Coastguard Worker} 455*1fa6dee9SAndroid Build Coastguard Worker 456*1fa6dee9SAndroid Build Coastguard Worker// Configurable can wrap the type of a blueprint property, 457*1fa6dee9SAndroid Build Coastguard Worker// in order to allow select statements to be used in bp files 458*1fa6dee9SAndroid Build Coastguard Worker// for that property. For example, for the property struct: 459*1fa6dee9SAndroid Build Coastguard Worker// 460*1fa6dee9SAndroid Build Coastguard Worker// my_props { 461*1fa6dee9SAndroid Build Coastguard Worker// Property_a: string, 462*1fa6dee9SAndroid Build Coastguard Worker// Property_b: Configurable[string], 463*1fa6dee9SAndroid Build Coastguard Worker// } 464*1fa6dee9SAndroid Build Coastguard Worker// 465*1fa6dee9SAndroid Build Coastguard Worker// property_b can then use select statements: 466*1fa6dee9SAndroid Build Coastguard Worker// 467*1fa6dee9SAndroid Build Coastguard Worker// my_module { 468*1fa6dee9SAndroid Build Coastguard Worker// property_a: "foo" 469*1fa6dee9SAndroid Build Coastguard Worker// property_b: select(soong_config_variable("my_namespace", "my_variable"), { 470*1fa6dee9SAndroid Build Coastguard Worker// "value_1": "bar", 471*1fa6dee9SAndroid Build Coastguard Worker// "value_2": "baz", 472*1fa6dee9SAndroid Build Coastguard Worker// default: "qux", 473*1fa6dee9SAndroid Build Coastguard Worker// }) 474*1fa6dee9SAndroid Build Coastguard Worker// } 475*1fa6dee9SAndroid Build Coastguard Worker// 476*1fa6dee9SAndroid Build Coastguard Worker// The configurable property holds all the branches of the select 477*1fa6dee9SAndroid Build Coastguard Worker// statement in the bp file. To extract the final value, you must 478*1fa6dee9SAndroid Build Coastguard Worker// call Evaluate() on the configurable property. 479*1fa6dee9SAndroid Build Coastguard Worker// 480*1fa6dee9SAndroid Build Coastguard Worker// All configurable properties support being unset, so there is 481*1fa6dee9SAndroid Build Coastguard Worker// no need to use a pointer type like Configurable[*string]. 482*1fa6dee9SAndroid Build Coastguard Workertype Configurable[T ConfigurableElements] struct { 483*1fa6dee9SAndroid Build Coastguard Worker marker configurableMarker 484*1fa6dee9SAndroid Build Coastguard Worker propertyName string 485*1fa6dee9SAndroid Build Coastguard Worker inner *configurableInner[T] 486*1fa6dee9SAndroid Build Coastguard Worker // See Configurable.evaluate for a description of the postProcessor algorithm and 487*1fa6dee9SAndroid Build Coastguard Worker // why this is a 2d list 488*1fa6dee9SAndroid Build Coastguard Worker postProcessors *[][]postProcessor[T] 489*1fa6dee9SAndroid Build Coastguard Worker} 490*1fa6dee9SAndroid Build Coastguard Worker 491*1fa6dee9SAndroid Build Coastguard Workertype postProcessor[T ConfigurableElements] struct { 492*1fa6dee9SAndroid Build Coastguard Worker f func(T) T 493*1fa6dee9SAndroid Build Coastguard Worker // start and end represent the range of configurableInners 494*1fa6dee9SAndroid Build Coastguard Worker // that this postprocessor is applied to. When appending two configurables 495*1fa6dee9SAndroid Build Coastguard Worker // together, the start and end values will stay the same for the left 496*1fa6dee9SAndroid Build Coastguard Worker // configurable's postprocessors, but the rights will be rebased by the 497*1fa6dee9SAndroid Build Coastguard Worker // number of configurableInners in the left configurable. This way 498*1fa6dee9SAndroid Build Coastguard Worker // the postProcessors still only apply to the configurableInners they 499*1fa6dee9SAndroid Build Coastguard Worker // origionally applied to before the appending. 500*1fa6dee9SAndroid Build Coastguard Worker start int 501*1fa6dee9SAndroid Build Coastguard Worker end int 502*1fa6dee9SAndroid Build Coastguard Worker} 503*1fa6dee9SAndroid Build Coastguard Worker 504*1fa6dee9SAndroid Build Coastguard Workertype configurableInner[T ConfigurableElements] struct { 505*1fa6dee9SAndroid Build Coastguard Worker single singleConfigurable[T] 506*1fa6dee9SAndroid Build Coastguard Worker replace bool 507*1fa6dee9SAndroid Build Coastguard Worker next *configurableInner[T] 508*1fa6dee9SAndroid Build Coastguard Worker} 509*1fa6dee9SAndroid Build Coastguard Worker 510*1fa6dee9SAndroid Build Coastguard Worker// singleConfigurable must be immutable so it can be reused 511*1fa6dee9SAndroid Build Coastguard Worker// between multiple configurables 512*1fa6dee9SAndroid Build Coastguard Workertype singleConfigurable[T ConfigurableElements] struct { 513*1fa6dee9SAndroid Build Coastguard Worker conditions []ConfigurableCondition 514*1fa6dee9SAndroid Build Coastguard Worker cases []ConfigurableCase[T] 515*1fa6dee9SAndroid Build Coastguard Worker scope *parser.Scope 516*1fa6dee9SAndroid Build Coastguard Worker} 517*1fa6dee9SAndroid Build Coastguard Worker 518*1fa6dee9SAndroid Build Coastguard Worker// Ignore the warning about the unused marker variable, it's used via reflection 519*1fa6dee9SAndroid Build Coastguard Workervar _ configurableMarker = Configurable[string]{}.marker 520*1fa6dee9SAndroid Build Coastguard Worker 521*1fa6dee9SAndroid Build Coastguard Workerfunc NewConfigurable[T ConfigurableElements](conditions []ConfigurableCondition, cases []ConfigurableCase[T]) Configurable[T] { 522*1fa6dee9SAndroid Build Coastguard Worker for _, c := range cases { 523*1fa6dee9SAndroid Build Coastguard Worker if len(c.patterns) != len(conditions) { 524*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Sprintf("All configurables cases must have as many patterns as the configurable has conditions. Expected: %d, found: %d", len(conditions), len(c.patterns))) 525*1fa6dee9SAndroid Build Coastguard Worker } 526*1fa6dee9SAndroid Build Coastguard Worker } 527*1fa6dee9SAndroid Build Coastguard Worker // Clone the slices so they can't be modified from soong 528*1fa6dee9SAndroid Build Coastguard Worker conditions = slices.Clone(conditions) 529*1fa6dee9SAndroid Build Coastguard Worker cases = slices.Clone(cases) 530*1fa6dee9SAndroid Build Coastguard Worker var zeroPostProcessors [][]postProcessor[T] 531*1fa6dee9SAndroid Build Coastguard Worker return Configurable[T]{ 532*1fa6dee9SAndroid Build Coastguard Worker inner: &configurableInner[T]{ 533*1fa6dee9SAndroid Build Coastguard Worker single: singleConfigurable[T]{ 534*1fa6dee9SAndroid Build Coastguard Worker conditions: conditions, 535*1fa6dee9SAndroid Build Coastguard Worker cases: cases, 536*1fa6dee9SAndroid Build Coastguard Worker }, 537*1fa6dee9SAndroid Build Coastguard Worker }, 538*1fa6dee9SAndroid Build Coastguard Worker postProcessors: &zeroPostProcessors, 539*1fa6dee9SAndroid Build Coastguard Worker } 540*1fa6dee9SAndroid Build Coastguard Worker} 541*1fa6dee9SAndroid Build Coastguard Worker 542*1fa6dee9SAndroid Build Coastguard Workerfunc NewSimpleConfigurable[T ConfigurableElements](value T) Configurable[T] { 543*1fa6dee9SAndroid Build Coastguard Worker return NewConfigurable(nil, []ConfigurableCase[T]{ 544*1fa6dee9SAndroid Build Coastguard Worker NewConfigurableCase(nil, &value), 545*1fa6dee9SAndroid Build Coastguard Worker }) 546*1fa6dee9SAndroid Build Coastguard Worker} 547*1fa6dee9SAndroid Build Coastguard Worker 548*1fa6dee9SAndroid Build Coastguard Workerfunc newConfigurableWithPropertyName[T ConfigurableElements](propertyName string, conditions []ConfigurableCondition, cases []ConfigurableCase[T], addScope bool) Configurable[T] { 549*1fa6dee9SAndroid Build Coastguard Worker result := NewConfigurable(conditions, cases) 550*1fa6dee9SAndroid Build Coastguard Worker result.propertyName = propertyName 551*1fa6dee9SAndroid Build Coastguard Worker if addScope { 552*1fa6dee9SAndroid Build Coastguard Worker for curr := result.inner; curr != nil; curr = curr.next { 553*1fa6dee9SAndroid Build Coastguard Worker curr.single.scope = parser.NewScope(nil) 554*1fa6dee9SAndroid Build Coastguard Worker } 555*1fa6dee9SAndroid Build Coastguard Worker } 556*1fa6dee9SAndroid Build Coastguard Worker return result 557*1fa6dee9SAndroid Build Coastguard Worker} 558*1fa6dee9SAndroid Build Coastguard Worker 559*1fa6dee9SAndroid Build Coastguard Workerfunc (c *Configurable[T]) AppendSimpleValue(value T) { 560*1fa6dee9SAndroid Build Coastguard Worker value = copyConfiguredValue(value) 561*1fa6dee9SAndroid Build Coastguard Worker // This may be a property that was never initialized from a bp file 562*1fa6dee9SAndroid Build Coastguard Worker if c.inner == nil { 563*1fa6dee9SAndroid Build Coastguard Worker c.initialize(nil, "", nil, []ConfigurableCase[T]{{ 564*1fa6dee9SAndroid Build Coastguard Worker value: configuredValueToExpression(value), 565*1fa6dee9SAndroid Build Coastguard Worker }}) 566*1fa6dee9SAndroid Build Coastguard Worker return 567*1fa6dee9SAndroid Build Coastguard Worker } 568*1fa6dee9SAndroid Build Coastguard Worker c.inner.appendSimpleValue(value) 569*1fa6dee9SAndroid Build Coastguard Worker} 570*1fa6dee9SAndroid Build Coastguard Worker 571*1fa6dee9SAndroid Build Coastguard Worker// AddPostProcessor adds a function that will modify the result of 572*1fa6dee9SAndroid Build Coastguard Worker// Get() when Get() is called. It operates on all the current contents 573*1fa6dee9SAndroid Build Coastguard Worker// of the Configurable property, but if other values are appended to 574*1fa6dee9SAndroid Build Coastguard Worker// the Configurable property afterwards, the postProcessor will not run 575*1fa6dee9SAndroid Build Coastguard Worker// on them. This can be useful to essentially modify a configurable 576*1fa6dee9SAndroid Build Coastguard Worker// property without evaluating it. 577*1fa6dee9SAndroid Build Coastguard Workerfunc (c *Configurable[T]) AddPostProcessor(p func(T) T) { 578*1fa6dee9SAndroid Build Coastguard Worker // Add the new postProcessor on top of the tallest stack of postProcessors. 579*1fa6dee9SAndroid Build Coastguard Worker // See Configurable.evaluate for more details on the postProcessors algorithm 580*1fa6dee9SAndroid Build Coastguard Worker // and data structure. 581*1fa6dee9SAndroid Build Coastguard Worker num_links := c.inner.numLinks() 582*1fa6dee9SAndroid Build Coastguard Worker if c.postProcessors == nil { 583*1fa6dee9SAndroid Build Coastguard Worker var nilCases []ConfigurableCase[T] 584*1fa6dee9SAndroid Build Coastguard Worker c.initialize(nil, "", nil, nilCases) 585*1fa6dee9SAndroid Build Coastguard Worker } 586*1fa6dee9SAndroid Build Coastguard Worker if len(*c.postProcessors) == 0 { 587*1fa6dee9SAndroid Build Coastguard Worker *c.postProcessors = [][]postProcessor[T]{{{ 588*1fa6dee9SAndroid Build Coastguard Worker f: p, 589*1fa6dee9SAndroid Build Coastguard Worker start: 0, 590*1fa6dee9SAndroid Build Coastguard Worker end: num_links, 591*1fa6dee9SAndroid Build Coastguard Worker }}} 592*1fa6dee9SAndroid Build Coastguard Worker } else { 593*1fa6dee9SAndroid Build Coastguard Worker deepestI := 0 594*1fa6dee9SAndroid Build Coastguard Worker deepestDepth := 0 595*1fa6dee9SAndroid Build Coastguard Worker for i := 0; i < len(*c.postProcessors); i++ { 596*1fa6dee9SAndroid Build Coastguard Worker if len((*c.postProcessors)[i]) > deepestDepth { 597*1fa6dee9SAndroid Build Coastguard Worker deepestDepth = len((*c.postProcessors)[i]) 598*1fa6dee9SAndroid Build Coastguard Worker deepestI = i 599*1fa6dee9SAndroid Build Coastguard Worker } 600*1fa6dee9SAndroid Build Coastguard Worker } 601*1fa6dee9SAndroid Build Coastguard Worker (*c.postProcessors)[deepestI] = append((*c.postProcessors)[deepestI], postProcessor[T]{ 602*1fa6dee9SAndroid Build Coastguard Worker f: p, 603*1fa6dee9SAndroid Build Coastguard Worker start: 0, 604*1fa6dee9SAndroid Build Coastguard Worker end: num_links, 605*1fa6dee9SAndroid Build Coastguard Worker }) 606*1fa6dee9SAndroid Build Coastguard Worker } 607*1fa6dee9SAndroid Build Coastguard Worker} 608*1fa6dee9SAndroid Build Coastguard Worker 609*1fa6dee9SAndroid Build Coastguard Worker// Get returns the final value for the configurable property. 610*1fa6dee9SAndroid Build Coastguard Worker// A configurable property may be unset, in which case Get will return nil. 611*1fa6dee9SAndroid Build Coastguard Workerfunc (c *Configurable[T]) Get(evaluator ConfigurableEvaluator) ConfigurableOptional[T] { 612*1fa6dee9SAndroid Build Coastguard Worker result := c.evaluate(c.propertyName, evaluator) 613*1fa6dee9SAndroid Build Coastguard Worker return configuredValuePtrToOptional(result) 614*1fa6dee9SAndroid Build Coastguard Worker} 615*1fa6dee9SAndroid Build Coastguard Worker 616*1fa6dee9SAndroid Build Coastguard Worker// GetOrDefault is the same as Get, but will return the provided default value if the property was unset. 617*1fa6dee9SAndroid Build Coastguard Workerfunc (c *Configurable[T]) GetOrDefault(evaluator ConfigurableEvaluator, defaultValue T) T { 618*1fa6dee9SAndroid Build Coastguard Worker result := c.evaluate(c.propertyName, evaluator) 619*1fa6dee9SAndroid Build Coastguard Worker if result != nil { 620*1fa6dee9SAndroid Build Coastguard Worker // Copy the result so that it can't be changed from soong 621*1fa6dee9SAndroid Build Coastguard Worker return copyConfiguredValue(*result) 622*1fa6dee9SAndroid Build Coastguard Worker } 623*1fa6dee9SAndroid Build Coastguard Worker return defaultValue 624*1fa6dee9SAndroid Build Coastguard Worker} 625*1fa6dee9SAndroid Build Coastguard Worker 626*1fa6dee9SAndroid Build Coastguard Workertype valueAndIndices[T ConfigurableElements] struct { 627*1fa6dee9SAndroid Build Coastguard Worker value *T 628*1fa6dee9SAndroid Build Coastguard Worker replace bool 629*1fa6dee9SAndroid Build Coastguard Worker // Similar to start/end in postProcessor, these represent the origional 630*1fa6dee9SAndroid Build Coastguard Worker // range or configurableInners that this merged group represents. It's needed 631*1fa6dee9SAndroid Build Coastguard Worker // in order to apply recursive postProcessors to only the relevant 632*1fa6dee9SAndroid Build Coastguard Worker // configurableInners, even after those configurableInners have been merged 633*1fa6dee9SAndroid Build Coastguard Worker // in order to apply an earlier postProcessor. 634*1fa6dee9SAndroid Build Coastguard Worker start int 635*1fa6dee9SAndroid Build Coastguard Worker end int 636*1fa6dee9SAndroid Build Coastguard Worker} 637*1fa6dee9SAndroid Build Coastguard Worker 638*1fa6dee9SAndroid Build Coastguard Workerfunc (c *Configurable[T]) evaluate(propertyName string, evaluator ConfigurableEvaluator) *T { 639*1fa6dee9SAndroid Build Coastguard Worker if c.inner == nil { 640*1fa6dee9SAndroid Build Coastguard Worker return nil 641*1fa6dee9SAndroid Build Coastguard Worker } 642*1fa6dee9SAndroid Build Coastguard Worker 643*1fa6dee9SAndroid Build Coastguard Worker if len(*c.postProcessors) == 0 { 644*1fa6dee9SAndroid Build Coastguard Worker // Use a simpler algorithm if there are no postprocessors 645*1fa6dee9SAndroid Build Coastguard Worker return c.inner.evaluate(propertyName, evaluator) 646*1fa6dee9SAndroid Build Coastguard Worker } 647*1fa6dee9SAndroid Build Coastguard Worker 648*1fa6dee9SAndroid Build Coastguard Worker // The basic idea around evaluating with postprocessors is that each individual 649*1fa6dee9SAndroid Build Coastguard Worker // node in the chain (each configurableInner) is first evaluated, and then when 650*1fa6dee9SAndroid Build Coastguard Worker // a postprocessor operates on a certain range, that range is merged before passing 651*1fa6dee9SAndroid Build Coastguard Worker // it to the postprocessor. We want postProcessors to only accept a final merged 652*1fa6dee9SAndroid Build Coastguard Worker // value instead of a linked list, but at the same time, only operate over a portion 653*1fa6dee9SAndroid Build Coastguard Worker // of the list. If more configurables are appended onto this one, their values won't 654*1fa6dee9SAndroid Build Coastguard Worker // be operated on by the existing postProcessors, but they may have their own 655*1fa6dee9SAndroid Build Coastguard Worker // postprocessors. 656*1fa6dee9SAndroid Build Coastguard Worker // 657*1fa6dee9SAndroid Build Coastguard Worker // _____________________ 658*1fa6dee9SAndroid Build Coastguard Worker // | __________| 659*1fa6dee9SAndroid Build Coastguard Worker // ______ | _____| ___ 660*1fa6dee9SAndroid Build Coastguard Worker // | | | | | | 661*1fa6dee9SAndroid Build Coastguard Worker // a -> b -> c -> d -> e -> f -> g 662*1fa6dee9SAndroid Build Coastguard Worker // 663*1fa6dee9SAndroid Build Coastguard Worker // In this diagram, the letters along the bottom is the chain of configurableInners. 664*1fa6dee9SAndroid Build Coastguard Worker // The brackets on top represent postprocessors, where higher brackets are processed 665*1fa6dee9SAndroid Build Coastguard Worker // after lower ones. 666*1fa6dee9SAndroid Build Coastguard Worker // 667*1fa6dee9SAndroid Build Coastguard Worker // To evaluate this example, first we evaluate the raw values for all nodes a->g. 668*1fa6dee9SAndroid Build Coastguard Worker // Then we merge nodes a/b and d/e and apply the postprocessors to their merged values, 669*1fa6dee9SAndroid Build Coastguard Worker // and also to g. Those merged and postprocessed nodes are then reinserted into the 670*1fa6dee9SAndroid Build Coastguard Worker // list, and we move on to doing the higher level postprocessors (starting with the c->e one) 671*1fa6dee9SAndroid Build Coastguard Worker // in the same way. When all postprocessors are done, a final merge is done on anything 672*1fa6dee9SAndroid Build Coastguard Worker // leftover. 673*1fa6dee9SAndroid Build Coastguard Worker // 674*1fa6dee9SAndroid Build Coastguard Worker // The Configurable.postProcessors field is a 2d array to represent this hierarchy. 675*1fa6dee9SAndroid Build Coastguard Worker // The outer index moves right on this graph, the inner index goes up. 676*1fa6dee9SAndroid Build Coastguard Worker // When adding a new postProcessor, it will always be the last postProcessor to run 677*1fa6dee9SAndroid Build Coastguard Worker // until another is added or another configurable is appended. So in AddPostProcessor(), 678*1fa6dee9SAndroid Build Coastguard Worker // we add it to the tallest existing stack. 679*1fa6dee9SAndroid Build Coastguard Worker 680*1fa6dee9SAndroid Build Coastguard Worker var currentValues []valueAndIndices[T] 681*1fa6dee9SAndroid Build Coastguard Worker for curr, i := c.inner, 0; curr != nil; curr, i = curr.next, i+1 { 682*1fa6dee9SAndroid Build Coastguard Worker value := curr.single.evaluateNonTransitive(propertyName, evaluator) 683*1fa6dee9SAndroid Build Coastguard Worker currentValues = append(currentValues, valueAndIndices[T]{ 684*1fa6dee9SAndroid Build Coastguard Worker value: value, 685*1fa6dee9SAndroid Build Coastguard Worker replace: curr.replace, 686*1fa6dee9SAndroid Build Coastguard Worker start: i, 687*1fa6dee9SAndroid Build Coastguard Worker end: i + 1, 688*1fa6dee9SAndroid Build Coastguard Worker }) 689*1fa6dee9SAndroid Build Coastguard Worker } 690*1fa6dee9SAndroid Build Coastguard Worker 691*1fa6dee9SAndroid Build Coastguard Worker if c.postProcessors == nil || len(*c.postProcessors) == 0 { 692*1fa6dee9SAndroid Build Coastguard Worker return mergeValues(currentValues).value 693*1fa6dee9SAndroid Build Coastguard Worker } 694*1fa6dee9SAndroid Build Coastguard Worker 695*1fa6dee9SAndroid Build Coastguard Worker foundPostProcessor := true 696*1fa6dee9SAndroid Build Coastguard Worker for depth := 0; foundPostProcessor; depth++ { 697*1fa6dee9SAndroid Build Coastguard Worker foundPostProcessor = false 698*1fa6dee9SAndroid Build Coastguard Worker var newValues []valueAndIndices[T] 699*1fa6dee9SAndroid Build Coastguard Worker i := 0 700*1fa6dee9SAndroid Build Coastguard Worker for _, postProcessorGroup := range *c.postProcessors { 701*1fa6dee9SAndroid Build Coastguard Worker if len(postProcessorGroup) > depth { 702*1fa6dee9SAndroid Build Coastguard Worker foundPostProcessor = true 703*1fa6dee9SAndroid Build Coastguard Worker postProcessor := postProcessorGroup[depth] 704*1fa6dee9SAndroid Build Coastguard Worker startI := 0 705*1fa6dee9SAndroid Build Coastguard Worker endI := 0 706*1fa6dee9SAndroid Build Coastguard Worker for currentValues[startI].start < postProcessor.start { 707*1fa6dee9SAndroid Build Coastguard Worker startI++ 708*1fa6dee9SAndroid Build Coastguard Worker } 709*1fa6dee9SAndroid Build Coastguard Worker for currentValues[endI].end < postProcessor.end { 710*1fa6dee9SAndroid Build Coastguard Worker endI++ 711*1fa6dee9SAndroid Build Coastguard Worker } 712*1fa6dee9SAndroid Build Coastguard Worker endI++ 713*1fa6dee9SAndroid Build Coastguard Worker newValues = append(newValues, currentValues[i:startI]...) 714*1fa6dee9SAndroid Build Coastguard Worker merged := mergeValues(currentValues[startI:endI]) 715*1fa6dee9SAndroid Build Coastguard Worker if merged.value != nil { 716*1fa6dee9SAndroid Build Coastguard Worker processed := postProcessor.f(*merged.value) 717*1fa6dee9SAndroid Build Coastguard Worker merged.value = &processed 718*1fa6dee9SAndroid Build Coastguard Worker } 719*1fa6dee9SAndroid Build Coastguard Worker newValues = append(newValues, merged) 720*1fa6dee9SAndroid Build Coastguard Worker i = endI 721*1fa6dee9SAndroid Build Coastguard Worker } 722*1fa6dee9SAndroid Build Coastguard Worker } 723*1fa6dee9SAndroid Build Coastguard Worker newValues = append(newValues, currentValues[i:]...) 724*1fa6dee9SAndroid Build Coastguard Worker currentValues = newValues 725*1fa6dee9SAndroid Build Coastguard Worker } 726*1fa6dee9SAndroid Build Coastguard Worker 727*1fa6dee9SAndroid Build Coastguard Worker return mergeValues(currentValues).value 728*1fa6dee9SAndroid Build Coastguard Worker} 729*1fa6dee9SAndroid Build Coastguard Worker 730*1fa6dee9SAndroid Build Coastguard Workerfunc mergeValues[T ConfigurableElements](values []valueAndIndices[T]) valueAndIndices[T] { 731*1fa6dee9SAndroid Build Coastguard Worker if len(values) < 0 { 732*1fa6dee9SAndroid Build Coastguard Worker panic("Expected at least 1 value in mergeValues") 733*1fa6dee9SAndroid Build Coastguard Worker } 734*1fa6dee9SAndroid Build Coastguard Worker result := values[0] 735*1fa6dee9SAndroid Build Coastguard Worker for i := 1; i < len(values); i++ { 736*1fa6dee9SAndroid Build Coastguard Worker if result.replace { 737*1fa6dee9SAndroid Build Coastguard Worker result.value = replaceConfiguredValues(result.value, values[i].value) 738*1fa6dee9SAndroid Build Coastguard Worker } else { 739*1fa6dee9SAndroid Build Coastguard Worker result.value = appendConfiguredValues(result.value, values[i].value) 740*1fa6dee9SAndroid Build Coastguard Worker } 741*1fa6dee9SAndroid Build Coastguard Worker result.end = values[i].end 742*1fa6dee9SAndroid Build Coastguard Worker result.replace = values[i].replace 743*1fa6dee9SAndroid Build Coastguard Worker } 744*1fa6dee9SAndroid Build Coastguard Worker return result 745*1fa6dee9SAndroid Build Coastguard Worker} 746*1fa6dee9SAndroid Build Coastguard Worker 747*1fa6dee9SAndroid Build Coastguard Workerfunc (c *configurableInner[T]) evaluate(propertyName string, evaluator ConfigurableEvaluator) *T { 748*1fa6dee9SAndroid Build Coastguard Worker if c == nil { 749*1fa6dee9SAndroid Build Coastguard Worker return nil 750*1fa6dee9SAndroid Build Coastguard Worker } 751*1fa6dee9SAndroid Build Coastguard Worker if c.next == nil { 752*1fa6dee9SAndroid Build Coastguard Worker return c.single.evaluateNonTransitive(propertyName, evaluator) 753*1fa6dee9SAndroid Build Coastguard Worker } 754*1fa6dee9SAndroid Build Coastguard Worker if c.replace { 755*1fa6dee9SAndroid Build Coastguard Worker return replaceConfiguredValues( 756*1fa6dee9SAndroid Build Coastguard Worker c.single.evaluateNonTransitive(propertyName, evaluator), 757*1fa6dee9SAndroid Build Coastguard Worker c.next.evaluate(propertyName, evaluator), 758*1fa6dee9SAndroid Build Coastguard Worker ) 759*1fa6dee9SAndroid Build Coastguard Worker } else { 760*1fa6dee9SAndroid Build Coastguard Worker return appendConfiguredValues( 761*1fa6dee9SAndroid Build Coastguard Worker c.single.evaluateNonTransitive(propertyName, evaluator), 762*1fa6dee9SAndroid Build Coastguard Worker c.next.evaluate(propertyName, evaluator), 763*1fa6dee9SAndroid Build Coastguard Worker ) 764*1fa6dee9SAndroid Build Coastguard Worker } 765*1fa6dee9SAndroid Build Coastguard Worker} 766*1fa6dee9SAndroid Build Coastguard Worker 767*1fa6dee9SAndroid Build Coastguard Workerfunc (c *singleConfigurable[T]) evaluateNonTransitive(propertyName string, evaluator ConfigurableEvaluator) *T { 768*1fa6dee9SAndroid Build Coastguard Worker for i, case_ := range c.cases { 769*1fa6dee9SAndroid Build Coastguard Worker if len(c.conditions) != len(case_.patterns) { 770*1fa6dee9SAndroid Build Coastguard Worker evaluator.PropertyErrorf(propertyName, "Expected each case to have as many patterns as conditions. conditions: %d, len(cases[%d].patterns): %d", len(c.conditions), i, len(case_.patterns)) 771*1fa6dee9SAndroid Build Coastguard Worker return nil 772*1fa6dee9SAndroid Build Coastguard Worker } 773*1fa6dee9SAndroid Build Coastguard Worker } 774*1fa6dee9SAndroid Build Coastguard Worker if len(c.conditions) == 0 { 775*1fa6dee9SAndroid Build Coastguard Worker if len(c.cases) == 0 { 776*1fa6dee9SAndroid Build Coastguard Worker return nil 777*1fa6dee9SAndroid Build Coastguard Worker } else if len(c.cases) == 1 { 778*1fa6dee9SAndroid Build Coastguard Worker if result, err := expressionToConfiguredValue[T](c.cases[0].value, c.scope); err != nil { 779*1fa6dee9SAndroid Build Coastguard Worker evaluator.PropertyErrorf(propertyName, "%s", err.Error()) 780*1fa6dee9SAndroid Build Coastguard Worker return nil 781*1fa6dee9SAndroid Build Coastguard Worker } else { 782*1fa6dee9SAndroid Build Coastguard Worker return result 783*1fa6dee9SAndroid Build Coastguard Worker } 784*1fa6dee9SAndroid Build Coastguard Worker } else { 785*1fa6dee9SAndroid Build Coastguard Worker evaluator.PropertyErrorf(propertyName, "Expected 0 or 1 branches in an unconfigured select, found %d", len(c.cases)) 786*1fa6dee9SAndroid Build Coastguard Worker return nil 787*1fa6dee9SAndroid Build Coastguard Worker } 788*1fa6dee9SAndroid Build Coastguard Worker } 789*1fa6dee9SAndroid Build Coastguard Worker values := make([]ConfigurableValue, len(c.conditions)) 790*1fa6dee9SAndroid Build Coastguard Worker for i, condition := range c.conditions { 791*1fa6dee9SAndroid Build Coastguard Worker values[i] = evaluator.EvaluateConfiguration(condition, propertyName) 792*1fa6dee9SAndroid Build Coastguard Worker } 793*1fa6dee9SAndroid Build Coastguard Worker foundMatch := false 794*1fa6dee9SAndroid Build Coastguard Worker nonMatchingIndex := 0 795*1fa6dee9SAndroid Build Coastguard Worker var result *T 796*1fa6dee9SAndroid Build Coastguard Worker for _, case_ := range c.cases { 797*1fa6dee9SAndroid Build Coastguard Worker allMatch := true 798*1fa6dee9SAndroid Build Coastguard Worker for i, pat := range case_.patterns { 799*1fa6dee9SAndroid Build Coastguard Worker if !pat.matchesValueType(values[i]) { 800*1fa6dee9SAndroid Build Coastguard Worker evaluator.PropertyErrorf(propertyName, "Expected all branches of a select on condition %s to have type %s, found %s", c.conditions[i].String(), values[i].typ.String(), pat.typ.String()) 801*1fa6dee9SAndroid Build Coastguard Worker return nil 802*1fa6dee9SAndroid Build Coastguard Worker } 803*1fa6dee9SAndroid Build Coastguard Worker if !pat.matchesValue(values[i]) { 804*1fa6dee9SAndroid Build Coastguard Worker allMatch = false 805*1fa6dee9SAndroid Build Coastguard Worker nonMatchingIndex = i 806*1fa6dee9SAndroid Build Coastguard Worker break 807*1fa6dee9SAndroid Build Coastguard Worker } 808*1fa6dee9SAndroid Build Coastguard Worker } 809*1fa6dee9SAndroid Build Coastguard Worker if allMatch && !foundMatch { 810*1fa6dee9SAndroid Build Coastguard Worker newScope := createScopeWithBindings(c.scope, case_.patterns, values) 811*1fa6dee9SAndroid Build Coastguard Worker if r, err := expressionToConfiguredValue[T](case_.value, newScope); err != nil { 812*1fa6dee9SAndroid Build Coastguard Worker evaluator.PropertyErrorf(propertyName, "%s", err.Error()) 813*1fa6dee9SAndroid Build Coastguard Worker return nil 814*1fa6dee9SAndroid Build Coastguard Worker } else { 815*1fa6dee9SAndroid Build Coastguard Worker result = r 816*1fa6dee9SAndroid Build Coastguard Worker } 817*1fa6dee9SAndroid Build Coastguard Worker foundMatch = true 818*1fa6dee9SAndroid Build Coastguard Worker } 819*1fa6dee9SAndroid Build Coastguard Worker } 820*1fa6dee9SAndroid Build Coastguard Worker if foundMatch { 821*1fa6dee9SAndroid Build Coastguard Worker return result 822*1fa6dee9SAndroid Build Coastguard Worker } 823*1fa6dee9SAndroid Build Coastguard Worker 824*1fa6dee9SAndroid Build Coastguard Worker evaluator.PropertyErrorf(propertyName, "%s had value %s, which was not handled by the select statement", c.conditions[nonMatchingIndex].String(), values[nonMatchingIndex].String()) 825*1fa6dee9SAndroid Build Coastguard Worker return nil 826*1fa6dee9SAndroid Build Coastguard Worker} 827*1fa6dee9SAndroid Build Coastguard Worker 828*1fa6dee9SAndroid Build Coastguard Workerfunc createScopeWithBindings(parent *parser.Scope, patterns []ConfigurablePattern, values []ConfigurableValue) *parser.Scope { 829*1fa6dee9SAndroid Build Coastguard Worker result := parent 830*1fa6dee9SAndroid Build Coastguard Worker for i, pattern := range patterns { 831*1fa6dee9SAndroid Build Coastguard Worker if pattern.binding != "" { 832*1fa6dee9SAndroid Build Coastguard Worker if result == parent { 833*1fa6dee9SAndroid Build Coastguard Worker result = parser.NewScope(parent) 834*1fa6dee9SAndroid Build Coastguard Worker } 835*1fa6dee9SAndroid Build Coastguard Worker err := result.HandleAssignment(&parser.Assignment{ 836*1fa6dee9SAndroid Build Coastguard Worker Name: pattern.binding, 837*1fa6dee9SAndroid Build Coastguard Worker Value: values[i].toExpression(), 838*1fa6dee9SAndroid Build Coastguard Worker Assigner: "=", 839*1fa6dee9SAndroid Build Coastguard Worker }) 840*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 841*1fa6dee9SAndroid Build Coastguard Worker // This shouldn't happen due to earlier validity checks 842*1fa6dee9SAndroid Build Coastguard Worker panic(err.Error()) 843*1fa6dee9SAndroid Build Coastguard Worker } 844*1fa6dee9SAndroid Build Coastguard Worker } 845*1fa6dee9SAndroid Build Coastguard Worker } 846*1fa6dee9SAndroid Build Coastguard Worker return result 847*1fa6dee9SAndroid Build Coastguard Worker} 848*1fa6dee9SAndroid Build Coastguard Worker 849*1fa6dee9SAndroid Build Coastguard Workerfunc appendConfiguredValues[T ConfigurableElements](a, b *T) *T { 850*1fa6dee9SAndroid Build Coastguard Worker if a == nil && b == nil { 851*1fa6dee9SAndroid Build Coastguard Worker return nil 852*1fa6dee9SAndroid Build Coastguard Worker } 853*1fa6dee9SAndroid Build Coastguard Worker switch any(a).(type) { 854*1fa6dee9SAndroid Build Coastguard Worker case *[]string: 855*1fa6dee9SAndroid Build Coastguard Worker var a2 []string 856*1fa6dee9SAndroid Build Coastguard Worker var b2 []string 857*1fa6dee9SAndroid Build Coastguard Worker if a != nil { 858*1fa6dee9SAndroid Build Coastguard Worker a2 = *any(a).(*[]string) 859*1fa6dee9SAndroid Build Coastguard Worker } 860*1fa6dee9SAndroid Build Coastguard Worker if b != nil { 861*1fa6dee9SAndroid Build Coastguard Worker b2 = *any(b).(*[]string) 862*1fa6dee9SAndroid Build Coastguard Worker } 863*1fa6dee9SAndroid Build Coastguard Worker result := make([]string, len(a2)+len(b2)) 864*1fa6dee9SAndroid Build Coastguard Worker idx := 0 865*1fa6dee9SAndroid Build Coastguard Worker for i := 0; i < len(a2); i++ { 866*1fa6dee9SAndroid Build Coastguard Worker result[idx] = a2[i] 867*1fa6dee9SAndroid Build Coastguard Worker idx += 1 868*1fa6dee9SAndroid Build Coastguard Worker } 869*1fa6dee9SAndroid Build Coastguard Worker for i := 0; i < len(b2); i++ { 870*1fa6dee9SAndroid Build Coastguard Worker result[idx] = b2[i] 871*1fa6dee9SAndroid Build Coastguard Worker idx += 1 872*1fa6dee9SAndroid Build Coastguard Worker } 873*1fa6dee9SAndroid Build Coastguard Worker return any(&result).(*T) 874*1fa6dee9SAndroid Build Coastguard Worker case *string: 875*1fa6dee9SAndroid Build Coastguard Worker a := String(any(a).(*string)) 876*1fa6dee9SAndroid Build Coastguard Worker b := String(any(b).(*string)) 877*1fa6dee9SAndroid Build Coastguard Worker result := a + b 878*1fa6dee9SAndroid Build Coastguard Worker return any(&result).(*T) 879*1fa6dee9SAndroid Build Coastguard Worker case *bool: 880*1fa6dee9SAndroid Build Coastguard Worker // Addition of bools will OR them together. This is inherited behavior 881*1fa6dee9SAndroid Build Coastguard Worker // from how proptools.ExtendBasicType works with non-configurable bools. 882*1fa6dee9SAndroid Build Coastguard Worker result := false 883*1fa6dee9SAndroid Build Coastguard Worker if a != nil { 884*1fa6dee9SAndroid Build Coastguard Worker result = result || *any(a).(*bool) 885*1fa6dee9SAndroid Build Coastguard Worker } 886*1fa6dee9SAndroid Build Coastguard Worker if b != nil { 887*1fa6dee9SAndroid Build Coastguard Worker result = result || *any(b).(*bool) 888*1fa6dee9SAndroid Build Coastguard Worker } 889*1fa6dee9SAndroid Build Coastguard Worker return any(&result).(*T) 890*1fa6dee9SAndroid Build Coastguard Worker default: 891*1fa6dee9SAndroid Build Coastguard Worker panic("Should be unreachable") 892*1fa6dee9SAndroid Build Coastguard Worker } 893*1fa6dee9SAndroid Build Coastguard Worker} 894*1fa6dee9SAndroid Build Coastguard Worker 895*1fa6dee9SAndroid Build Coastguard Workerfunc replaceConfiguredValues[T ConfigurableElements](a, b *T) *T { 896*1fa6dee9SAndroid Build Coastguard Worker if b != nil { 897*1fa6dee9SAndroid Build Coastguard Worker return b 898*1fa6dee9SAndroid Build Coastguard Worker } 899*1fa6dee9SAndroid Build Coastguard Worker return a 900*1fa6dee9SAndroid Build Coastguard Worker} 901*1fa6dee9SAndroid Build Coastguard Worker 902*1fa6dee9SAndroid Build Coastguard Worker// configurableReflection is an interface that exposes some methods that are 903*1fa6dee9SAndroid Build Coastguard Worker// helpful when working with reflect.Values of Configurable objects, used by 904*1fa6dee9SAndroid Build Coastguard Worker// the property unpacking code. You can't call unexported methods from reflection, 905*1fa6dee9SAndroid Build Coastguard Worker// (at least without unsafe pointer trickery) so this is the next best thing. 906*1fa6dee9SAndroid Build Coastguard Workertype configurableReflection interface { 907*1fa6dee9SAndroid Build Coastguard Worker setAppend(append any, replace bool, prepend bool) 908*1fa6dee9SAndroid Build Coastguard Worker configuredType() reflect.Type 909*1fa6dee9SAndroid Build Coastguard Worker clone() any 910*1fa6dee9SAndroid Build Coastguard Worker isEmpty() bool 911*1fa6dee9SAndroid Build Coastguard Worker printfInto(value string) error 912*1fa6dee9SAndroid Build Coastguard Worker toExpression() (*parser.Expression, error) 913*1fa6dee9SAndroid Build Coastguard Worker} 914*1fa6dee9SAndroid Build Coastguard Worker 915*1fa6dee9SAndroid Build Coastguard Worker// Same as configurableReflection, but since initialize needs to take a pointer 916*1fa6dee9SAndroid Build Coastguard Worker// to a Configurable, it was broken out into a separate interface. 917*1fa6dee9SAndroid Build Coastguard Workertype configurablePtrReflection interface { 918*1fa6dee9SAndroid Build Coastguard Worker initialize(scope *parser.Scope, propertyName string, conditions []ConfigurableCondition, cases any) 919*1fa6dee9SAndroid Build Coastguard Worker} 920*1fa6dee9SAndroid Build Coastguard Worker 921*1fa6dee9SAndroid Build Coastguard Workervar _ configurableReflection = Configurable[string]{} 922*1fa6dee9SAndroid Build Coastguard Workervar _ configurablePtrReflection = &Configurable[string]{} 923*1fa6dee9SAndroid Build Coastguard Worker 924*1fa6dee9SAndroid Build Coastguard Workerfunc (c *Configurable[T]) initialize(scope *parser.Scope, propertyName string, conditions []ConfigurableCondition, cases any) { 925*1fa6dee9SAndroid Build Coastguard Worker c.propertyName = propertyName 926*1fa6dee9SAndroid Build Coastguard Worker c.inner = &configurableInner[T]{ 927*1fa6dee9SAndroid Build Coastguard Worker single: singleConfigurable[T]{ 928*1fa6dee9SAndroid Build Coastguard Worker conditions: conditions, 929*1fa6dee9SAndroid Build Coastguard Worker cases: cases.([]ConfigurableCase[T]), 930*1fa6dee9SAndroid Build Coastguard Worker scope: scope, 931*1fa6dee9SAndroid Build Coastguard Worker }, 932*1fa6dee9SAndroid Build Coastguard Worker } 933*1fa6dee9SAndroid Build Coastguard Worker var postProcessors [][]postProcessor[T] 934*1fa6dee9SAndroid Build Coastguard Worker c.postProcessors = &postProcessors 935*1fa6dee9SAndroid Build Coastguard Worker} 936*1fa6dee9SAndroid Build Coastguard Worker 937*1fa6dee9SAndroid Build Coastguard Workerfunc (c *Configurable[T]) Append(other Configurable[T]) { 938*1fa6dee9SAndroid Build Coastguard Worker c.setAppend(other, false, false) 939*1fa6dee9SAndroid Build Coastguard Worker} 940*1fa6dee9SAndroid Build Coastguard Worker 941*1fa6dee9SAndroid Build Coastguard Workerfunc (c Configurable[T]) setAppend(append any, replace bool, prepend bool) { 942*1fa6dee9SAndroid Build Coastguard Worker a := append.(Configurable[T]) 943*1fa6dee9SAndroid Build Coastguard Worker if a.inner.isEmpty() { 944*1fa6dee9SAndroid Build Coastguard Worker return 945*1fa6dee9SAndroid Build Coastguard Worker } 946*1fa6dee9SAndroid Build Coastguard Worker 947*1fa6dee9SAndroid Build Coastguard Worker if prepend { 948*1fa6dee9SAndroid Build Coastguard Worker newBase := a.inner.numLinks() 949*1fa6dee9SAndroid Build Coastguard Worker *c.postProcessors = appendPostprocessors(*a.postProcessors, *c.postProcessors, newBase) 950*1fa6dee9SAndroid Build Coastguard Worker } else { 951*1fa6dee9SAndroid Build Coastguard Worker newBase := c.inner.numLinks() 952*1fa6dee9SAndroid Build Coastguard Worker *c.postProcessors = appendPostprocessors(*c.postProcessors, *a.postProcessors, newBase) 953*1fa6dee9SAndroid Build Coastguard Worker } 954*1fa6dee9SAndroid Build Coastguard Worker 955*1fa6dee9SAndroid Build Coastguard Worker c.inner.setAppend(a.inner, replace, prepend) 956*1fa6dee9SAndroid Build Coastguard Worker if c.inner == c.inner.next { 957*1fa6dee9SAndroid Build Coastguard Worker panic("pointer loop") 958*1fa6dee9SAndroid Build Coastguard Worker } 959*1fa6dee9SAndroid Build Coastguard Worker} 960*1fa6dee9SAndroid Build Coastguard Worker 961*1fa6dee9SAndroid Build Coastguard Workerfunc (c Configurable[T]) toExpression() (*parser.Expression, error) { 962*1fa6dee9SAndroid Build Coastguard Worker var err error 963*1fa6dee9SAndroid Build Coastguard Worker var result *parser.Select 964*1fa6dee9SAndroid Build Coastguard Worker var tail *parser.Select 965*1fa6dee9SAndroid Build Coastguard Worker for curr := c.inner; curr != nil; curr = curr.next { 966*1fa6dee9SAndroid Build Coastguard Worker if curr.replace == true { 967*1fa6dee9SAndroid Build Coastguard Worker return nil, fmt.Errorf("Cannot turn a configurable property with replacements into an expression; " + 968*1fa6dee9SAndroid Build Coastguard Worker "replacements can only be created via soong code / defaults squashing, not simply in a bp file") 969*1fa6dee9SAndroid Build Coastguard Worker } 970*1fa6dee9SAndroid Build Coastguard Worker if curr.single.isEmpty() { 971*1fa6dee9SAndroid Build Coastguard Worker continue 972*1fa6dee9SAndroid Build Coastguard Worker } 973*1fa6dee9SAndroid Build Coastguard Worker if result == nil { 974*1fa6dee9SAndroid Build Coastguard Worker result, err = curr.single.toExpression() 975*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 976*1fa6dee9SAndroid Build Coastguard Worker return nil, err 977*1fa6dee9SAndroid Build Coastguard Worker } 978*1fa6dee9SAndroid Build Coastguard Worker tail = result 979*1fa6dee9SAndroid Build Coastguard Worker } else { 980*1fa6dee9SAndroid Build Coastguard Worker tail.Append, err = curr.single.toExpression() 981*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 982*1fa6dee9SAndroid Build Coastguard Worker return nil, err 983*1fa6dee9SAndroid Build Coastguard Worker } 984*1fa6dee9SAndroid Build Coastguard Worker tail = tail.Append.(*parser.Select) 985*1fa6dee9SAndroid Build Coastguard Worker } 986*1fa6dee9SAndroid Build Coastguard Worker } 987*1fa6dee9SAndroid Build Coastguard Worker if result == nil { 988*1fa6dee9SAndroid Build Coastguard Worker return nil, nil 989*1fa6dee9SAndroid Build Coastguard Worker } 990*1fa6dee9SAndroid Build Coastguard Worker var result2 parser.Expression = result 991*1fa6dee9SAndroid Build Coastguard Worker return &result2, nil 992*1fa6dee9SAndroid Build Coastguard Worker} 993*1fa6dee9SAndroid Build Coastguard Worker 994*1fa6dee9SAndroid Build Coastguard Workerfunc appendPostprocessors[T ConfigurableElements](a, b [][]postProcessor[T], newBase int) [][]postProcessor[T] { 995*1fa6dee9SAndroid Build Coastguard Worker var result [][]postProcessor[T] 996*1fa6dee9SAndroid Build Coastguard Worker for i := 0; i < len(a); i++ { 997*1fa6dee9SAndroid Build Coastguard Worker result = append(result, slices.Clone(a[i])) 998*1fa6dee9SAndroid Build Coastguard Worker } 999*1fa6dee9SAndroid Build Coastguard Worker for i := 0; i < len(b); i++ { 1000*1fa6dee9SAndroid Build Coastguard Worker n := slices.Clone(b[i]) 1001*1fa6dee9SAndroid Build Coastguard Worker for j := 0; j < len(n); j++ { 1002*1fa6dee9SAndroid Build Coastguard Worker n[j].start += newBase 1003*1fa6dee9SAndroid Build Coastguard Worker n[j].end += newBase 1004*1fa6dee9SAndroid Build Coastguard Worker } 1005*1fa6dee9SAndroid Build Coastguard Worker result = append(result, n) 1006*1fa6dee9SAndroid Build Coastguard Worker } 1007*1fa6dee9SAndroid Build Coastguard Worker return result 1008*1fa6dee9SAndroid Build Coastguard Worker} 1009*1fa6dee9SAndroid Build Coastguard Worker 1010*1fa6dee9SAndroid Build Coastguard Workerfunc (c *configurableInner[T]) setAppend(append *configurableInner[T], replace bool, prepend bool) { 1011*1fa6dee9SAndroid Build Coastguard Worker if c.isEmpty() { 1012*1fa6dee9SAndroid Build Coastguard Worker *c = *append.clone() 1013*1fa6dee9SAndroid Build Coastguard Worker } else if prepend { 1014*1fa6dee9SAndroid Build Coastguard Worker if replace && c.alwaysHasValue() { 1015*1fa6dee9SAndroid Build Coastguard Worker // The current value would always override the prepended value, so don't do anything 1016*1fa6dee9SAndroid Build Coastguard Worker return 1017*1fa6dee9SAndroid Build Coastguard Worker } 1018*1fa6dee9SAndroid Build Coastguard Worker // We're going to replace the head node with the one from append, so allocate 1019*1fa6dee9SAndroid Build Coastguard Worker // a new one here. 1020*1fa6dee9SAndroid Build Coastguard Worker old := &configurableInner[T]{ 1021*1fa6dee9SAndroid Build Coastguard Worker single: c.single, 1022*1fa6dee9SAndroid Build Coastguard Worker replace: c.replace, 1023*1fa6dee9SAndroid Build Coastguard Worker next: c.next, 1024*1fa6dee9SAndroid Build Coastguard Worker } 1025*1fa6dee9SAndroid Build Coastguard Worker *c = *append.clone() 1026*1fa6dee9SAndroid Build Coastguard Worker curr := c 1027*1fa6dee9SAndroid Build Coastguard Worker for curr.next != nil { 1028*1fa6dee9SAndroid Build Coastguard Worker curr = curr.next 1029*1fa6dee9SAndroid Build Coastguard Worker } 1030*1fa6dee9SAndroid Build Coastguard Worker curr.next = old 1031*1fa6dee9SAndroid Build Coastguard Worker curr.replace = replace 1032*1fa6dee9SAndroid Build Coastguard Worker } else { 1033*1fa6dee9SAndroid Build Coastguard Worker // If we're replacing with something that always has a value set, 1034*1fa6dee9SAndroid Build Coastguard Worker // we can optimize the code by replacing our entire append chain here. 1035*1fa6dee9SAndroid Build Coastguard Worker if replace && append.alwaysHasValue() { 1036*1fa6dee9SAndroid Build Coastguard Worker *c = *append.clone() 1037*1fa6dee9SAndroid Build Coastguard Worker } else { 1038*1fa6dee9SAndroid Build Coastguard Worker curr := c 1039*1fa6dee9SAndroid Build Coastguard Worker for curr.next != nil { 1040*1fa6dee9SAndroid Build Coastguard Worker curr = curr.next 1041*1fa6dee9SAndroid Build Coastguard Worker } 1042*1fa6dee9SAndroid Build Coastguard Worker curr.next = append.clone() 1043*1fa6dee9SAndroid Build Coastguard Worker curr.replace = replace 1044*1fa6dee9SAndroid Build Coastguard Worker } 1045*1fa6dee9SAndroid Build Coastguard Worker } 1046*1fa6dee9SAndroid Build Coastguard Worker} 1047*1fa6dee9SAndroid Build Coastguard Worker 1048*1fa6dee9SAndroid Build Coastguard Workerfunc (c *configurableInner[T]) numLinks() int { 1049*1fa6dee9SAndroid Build Coastguard Worker result := 0 1050*1fa6dee9SAndroid Build Coastguard Worker for curr := c; curr != nil; curr = curr.next { 1051*1fa6dee9SAndroid Build Coastguard Worker result++ 1052*1fa6dee9SAndroid Build Coastguard Worker } 1053*1fa6dee9SAndroid Build Coastguard Worker return result 1054*1fa6dee9SAndroid Build Coastguard Worker} 1055*1fa6dee9SAndroid Build Coastguard Worker 1056*1fa6dee9SAndroid Build Coastguard Workerfunc (c *configurableInner[T]) appendSimpleValue(value T) { 1057*1fa6dee9SAndroid Build Coastguard Worker if c.next == nil { 1058*1fa6dee9SAndroid Build Coastguard Worker c.replace = false 1059*1fa6dee9SAndroid Build Coastguard Worker c.next = &configurableInner[T]{ 1060*1fa6dee9SAndroid Build Coastguard Worker single: singleConfigurable[T]{ 1061*1fa6dee9SAndroid Build Coastguard Worker cases: []ConfigurableCase[T]{{ 1062*1fa6dee9SAndroid Build Coastguard Worker value: configuredValueToExpression(value), 1063*1fa6dee9SAndroid Build Coastguard Worker }}, 1064*1fa6dee9SAndroid Build Coastguard Worker }, 1065*1fa6dee9SAndroid Build Coastguard Worker } 1066*1fa6dee9SAndroid Build Coastguard Worker } else { 1067*1fa6dee9SAndroid Build Coastguard Worker c.next.appendSimpleValue(value) 1068*1fa6dee9SAndroid Build Coastguard Worker } 1069*1fa6dee9SAndroid Build Coastguard Worker} 1070*1fa6dee9SAndroid Build Coastguard Worker 1071*1fa6dee9SAndroid Build Coastguard Workerfunc (c Configurable[T]) printfInto(value string) error { 1072*1fa6dee9SAndroid Build Coastguard Worker return c.inner.printfInto(value) 1073*1fa6dee9SAndroid Build Coastguard Worker} 1074*1fa6dee9SAndroid Build Coastguard Worker 1075*1fa6dee9SAndroid Build Coastguard Workerfunc (c *configurableInner[T]) printfInto(value string) error { 1076*1fa6dee9SAndroid Build Coastguard Worker for c != nil { 1077*1fa6dee9SAndroid Build Coastguard Worker if err := c.single.printfInto(value); err != nil { 1078*1fa6dee9SAndroid Build Coastguard Worker return err 1079*1fa6dee9SAndroid Build Coastguard Worker } 1080*1fa6dee9SAndroid Build Coastguard Worker c = c.next 1081*1fa6dee9SAndroid Build Coastguard Worker } 1082*1fa6dee9SAndroid Build Coastguard Worker return nil 1083*1fa6dee9SAndroid Build Coastguard Worker} 1084*1fa6dee9SAndroid Build Coastguard Worker 1085*1fa6dee9SAndroid Build Coastguard Workerfunc (c *singleConfigurable[T]) printfInto(value string) error { 1086*1fa6dee9SAndroid Build Coastguard Worker for _, c := range c.cases { 1087*1fa6dee9SAndroid Build Coastguard Worker if c.value == nil { 1088*1fa6dee9SAndroid Build Coastguard Worker continue 1089*1fa6dee9SAndroid Build Coastguard Worker } 1090*1fa6dee9SAndroid Build Coastguard Worker if err := c.value.PrintfInto(value); err != nil { 1091*1fa6dee9SAndroid Build Coastguard Worker return err 1092*1fa6dee9SAndroid Build Coastguard Worker } 1093*1fa6dee9SAndroid Build Coastguard Worker } 1094*1fa6dee9SAndroid Build Coastguard Worker return nil 1095*1fa6dee9SAndroid Build Coastguard Worker} 1096*1fa6dee9SAndroid Build Coastguard Worker 1097*1fa6dee9SAndroid Build Coastguard Workerfunc (c *singleConfigurable[T]) toExpression() (*parser.Select, error) { 1098*1fa6dee9SAndroid Build Coastguard Worker if c.scope != nil { 1099*1fa6dee9SAndroid Build Coastguard Worker return nil, fmt.Errorf("Cannot turn a select with a scope back into an expression") 1100*1fa6dee9SAndroid Build Coastguard Worker } 1101*1fa6dee9SAndroid Build Coastguard Worker var conditions []parser.ConfigurableCondition 1102*1fa6dee9SAndroid Build Coastguard Worker for _, cond := range c.conditions { 1103*1fa6dee9SAndroid Build Coastguard Worker conditions = append(conditions, cond.toParserConfigurableCondition()) 1104*1fa6dee9SAndroid Build Coastguard Worker } 1105*1fa6dee9SAndroid Build Coastguard Worker var cases []*parser.SelectCase 1106*1fa6dee9SAndroid Build Coastguard Worker for _, case_ := range c.cases { 1107*1fa6dee9SAndroid Build Coastguard Worker cases = append(cases, case_.toParserConfigurableCase()) 1108*1fa6dee9SAndroid Build Coastguard Worker } 1109*1fa6dee9SAndroid Build Coastguard Worker result := &parser.Select{ 1110*1fa6dee9SAndroid Build Coastguard Worker Conditions: conditions, 1111*1fa6dee9SAndroid Build Coastguard Worker Cases: cases, 1112*1fa6dee9SAndroid Build Coastguard Worker } 1113*1fa6dee9SAndroid Build Coastguard Worker return result, nil 1114*1fa6dee9SAndroid Build Coastguard Worker} 1115*1fa6dee9SAndroid Build Coastguard Worker 1116*1fa6dee9SAndroid Build Coastguard Workerfunc (c Configurable[T]) clone() any { 1117*1fa6dee9SAndroid Build Coastguard Worker var newPostProcessors *[][]postProcessor[T] 1118*1fa6dee9SAndroid Build Coastguard Worker if c.postProcessors != nil { 1119*1fa6dee9SAndroid Build Coastguard Worker x := appendPostprocessors(*c.postProcessors, nil, 0) 1120*1fa6dee9SAndroid Build Coastguard Worker newPostProcessors = &x 1121*1fa6dee9SAndroid Build Coastguard Worker } 1122*1fa6dee9SAndroid Build Coastguard Worker return Configurable[T]{ 1123*1fa6dee9SAndroid Build Coastguard Worker propertyName: c.propertyName, 1124*1fa6dee9SAndroid Build Coastguard Worker inner: c.inner.clone(), 1125*1fa6dee9SAndroid Build Coastguard Worker postProcessors: newPostProcessors, 1126*1fa6dee9SAndroid Build Coastguard Worker } 1127*1fa6dee9SAndroid Build Coastguard Worker} 1128*1fa6dee9SAndroid Build Coastguard Worker 1129*1fa6dee9SAndroid Build Coastguard Workerfunc (c Configurable[T]) Clone() Configurable[T] { 1130*1fa6dee9SAndroid Build Coastguard Worker return c.clone().(Configurable[T]) 1131*1fa6dee9SAndroid Build Coastguard Worker} 1132*1fa6dee9SAndroid Build Coastguard Worker 1133*1fa6dee9SAndroid Build Coastguard Workerfunc (c *configurableInner[T]) clone() *configurableInner[T] { 1134*1fa6dee9SAndroid Build Coastguard Worker if c == nil { 1135*1fa6dee9SAndroid Build Coastguard Worker return nil 1136*1fa6dee9SAndroid Build Coastguard Worker } 1137*1fa6dee9SAndroid Build Coastguard Worker return &configurableInner[T]{ 1138*1fa6dee9SAndroid Build Coastguard Worker // We don't need to clone the singleConfigurable because 1139*1fa6dee9SAndroid Build Coastguard Worker // it's supposed to be immutable 1140*1fa6dee9SAndroid Build Coastguard Worker single: c.single, 1141*1fa6dee9SAndroid Build Coastguard Worker replace: c.replace, 1142*1fa6dee9SAndroid Build Coastguard Worker next: c.next.clone(), 1143*1fa6dee9SAndroid Build Coastguard Worker } 1144*1fa6dee9SAndroid Build Coastguard Worker} 1145*1fa6dee9SAndroid Build Coastguard Worker 1146*1fa6dee9SAndroid Build Coastguard Workerfunc (c *configurableInner[T]) isEmpty() bool { 1147*1fa6dee9SAndroid Build Coastguard Worker if c == nil { 1148*1fa6dee9SAndroid Build Coastguard Worker return true 1149*1fa6dee9SAndroid Build Coastguard Worker } 1150*1fa6dee9SAndroid Build Coastguard Worker if !c.single.isEmpty() { 1151*1fa6dee9SAndroid Build Coastguard Worker return false 1152*1fa6dee9SAndroid Build Coastguard Worker } 1153*1fa6dee9SAndroid Build Coastguard Worker return c.next.isEmpty() 1154*1fa6dee9SAndroid Build Coastguard Worker} 1155*1fa6dee9SAndroid Build Coastguard Worker 1156*1fa6dee9SAndroid Build Coastguard Workerfunc (c Configurable[T]) isEmpty() bool { 1157*1fa6dee9SAndroid Build Coastguard Worker return c.inner.isEmpty() 1158*1fa6dee9SAndroid Build Coastguard Worker} 1159*1fa6dee9SAndroid Build Coastguard Worker 1160*1fa6dee9SAndroid Build Coastguard Workerfunc (c *singleConfigurable[T]) isEmpty() bool { 1161*1fa6dee9SAndroid Build Coastguard Worker if c == nil { 1162*1fa6dee9SAndroid Build Coastguard Worker return true 1163*1fa6dee9SAndroid Build Coastguard Worker } 1164*1fa6dee9SAndroid Build Coastguard Worker if len(c.cases) > 1 { 1165*1fa6dee9SAndroid Build Coastguard Worker return false 1166*1fa6dee9SAndroid Build Coastguard Worker } 1167*1fa6dee9SAndroid Build Coastguard Worker if len(c.cases) == 1 && c.cases[0].value != nil { 1168*1fa6dee9SAndroid Build Coastguard Worker if _, ok := c.cases[0].value.(*parser.UnsetProperty); ok { 1169*1fa6dee9SAndroid Build Coastguard Worker return true 1170*1fa6dee9SAndroid Build Coastguard Worker } 1171*1fa6dee9SAndroid Build Coastguard Worker return false 1172*1fa6dee9SAndroid Build Coastguard Worker } 1173*1fa6dee9SAndroid Build Coastguard Worker return true 1174*1fa6dee9SAndroid Build Coastguard Worker} 1175*1fa6dee9SAndroid Build Coastguard Worker 1176*1fa6dee9SAndroid Build Coastguard Workerfunc (c *configurableInner[T]) alwaysHasValue() bool { 1177*1fa6dee9SAndroid Build Coastguard Worker for curr := c; curr != nil; curr = curr.next { 1178*1fa6dee9SAndroid Build Coastguard Worker if curr.single.alwaysHasValue() { 1179*1fa6dee9SAndroid Build Coastguard Worker return true 1180*1fa6dee9SAndroid Build Coastguard Worker } 1181*1fa6dee9SAndroid Build Coastguard Worker } 1182*1fa6dee9SAndroid Build Coastguard Worker return false 1183*1fa6dee9SAndroid Build Coastguard Worker} 1184*1fa6dee9SAndroid Build Coastguard Worker 1185*1fa6dee9SAndroid Build Coastguard Workerfunc (c *singleConfigurable[T]) alwaysHasValue() bool { 1186*1fa6dee9SAndroid Build Coastguard Worker if len(c.cases) == 0 { 1187*1fa6dee9SAndroid Build Coastguard Worker return false 1188*1fa6dee9SAndroid Build Coastguard Worker } 1189*1fa6dee9SAndroid Build Coastguard Worker for _, c := range c.cases { 1190*1fa6dee9SAndroid Build Coastguard Worker if _, isUnset := c.value.(*parser.UnsetProperty); isUnset || c.value == nil { 1191*1fa6dee9SAndroid Build Coastguard Worker return false 1192*1fa6dee9SAndroid Build Coastguard Worker } 1193*1fa6dee9SAndroid Build Coastguard Worker } 1194*1fa6dee9SAndroid Build Coastguard Worker return true 1195*1fa6dee9SAndroid Build Coastguard Worker} 1196*1fa6dee9SAndroid Build Coastguard Worker 1197*1fa6dee9SAndroid Build Coastguard Workerfunc (c Configurable[T]) configuredType() reflect.Type { 1198*1fa6dee9SAndroid Build Coastguard Worker return reflect.TypeOf((*T)(nil)).Elem() 1199*1fa6dee9SAndroid Build Coastguard Worker} 1200*1fa6dee9SAndroid Build Coastguard Worker 1201*1fa6dee9SAndroid Build Coastguard Workerfunc expressionToConfiguredValue[T ConfigurableElements](expr parser.Expression, scope *parser.Scope) (*T, error) { 1202*1fa6dee9SAndroid Build Coastguard Worker expr, err := expr.Eval(scope) 1203*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 1204*1fa6dee9SAndroid Build Coastguard Worker return nil, err 1205*1fa6dee9SAndroid Build Coastguard Worker } 1206*1fa6dee9SAndroid Build Coastguard Worker switch e := expr.(type) { 1207*1fa6dee9SAndroid Build Coastguard Worker case *parser.UnsetProperty: 1208*1fa6dee9SAndroid Build Coastguard Worker return nil, nil 1209*1fa6dee9SAndroid Build Coastguard Worker case *parser.String: 1210*1fa6dee9SAndroid Build Coastguard Worker if result, ok := any(&e.Value).(*T); ok { 1211*1fa6dee9SAndroid Build Coastguard Worker return result, nil 1212*1fa6dee9SAndroid Build Coastguard Worker } else { 1213*1fa6dee9SAndroid Build Coastguard Worker return nil, fmt.Errorf("can't assign string value to %s property", configuredTypeToString[T]()) 1214*1fa6dee9SAndroid Build Coastguard Worker } 1215*1fa6dee9SAndroid Build Coastguard Worker case *parser.Bool: 1216*1fa6dee9SAndroid Build Coastguard Worker if result, ok := any(&e.Value).(*T); ok { 1217*1fa6dee9SAndroid Build Coastguard Worker return result, nil 1218*1fa6dee9SAndroid Build Coastguard Worker } else { 1219*1fa6dee9SAndroid Build Coastguard Worker return nil, fmt.Errorf("can't assign bool value to %s property", configuredTypeToString[T]()) 1220*1fa6dee9SAndroid Build Coastguard Worker } 1221*1fa6dee9SAndroid Build Coastguard Worker case *parser.List: 1222*1fa6dee9SAndroid Build Coastguard Worker result := make([]string, 0, len(e.Values)) 1223*1fa6dee9SAndroid Build Coastguard Worker for _, x := range e.Values { 1224*1fa6dee9SAndroid Build Coastguard Worker if y, ok := x.(*parser.String); ok { 1225*1fa6dee9SAndroid Build Coastguard Worker result = append(result, y.Value) 1226*1fa6dee9SAndroid Build Coastguard Worker } else { 1227*1fa6dee9SAndroid Build Coastguard Worker return nil, fmt.Errorf("expected list of strings but found list of %s", x.Type()) 1228*1fa6dee9SAndroid Build Coastguard Worker } 1229*1fa6dee9SAndroid Build Coastguard Worker } 1230*1fa6dee9SAndroid Build Coastguard Worker if result, ok := any(&result).(*T); ok { 1231*1fa6dee9SAndroid Build Coastguard Worker return result, nil 1232*1fa6dee9SAndroid Build Coastguard Worker } else { 1233*1fa6dee9SAndroid Build Coastguard Worker return nil, fmt.Errorf("can't assign list of strings to list of %s property", configuredTypeToString[T]()) 1234*1fa6dee9SAndroid Build Coastguard Worker } 1235*1fa6dee9SAndroid Build Coastguard Worker default: 1236*1fa6dee9SAndroid Build Coastguard Worker // If the expression was not evaluated beforehand we could hit this error even when the types match, 1237*1fa6dee9SAndroid Build Coastguard Worker // but that's an internal logic error. 1238*1fa6dee9SAndroid Build Coastguard Worker return nil, fmt.Errorf("expected %s but found %s (%#v)", configuredTypeToString[T](), expr.Type().String(), expr) 1239*1fa6dee9SAndroid Build Coastguard Worker } 1240*1fa6dee9SAndroid Build Coastguard Worker} 1241*1fa6dee9SAndroid Build Coastguard Worker 1242*1fa6dee9SAndroid Build Coastguard Workerfunc configuredValueToExpression[T ConfigurableElements](value T) parser.Expression { 1243*1fa6dee9SAndroid Build Coastguard Worker switch v := any(value).(type) { 1244*1fa6dee9SAndroid Build Coastguard Worker case string: 1245*1fa6dee9SAndroid Build Coastguard Worker return &parser.String{Value: v} 1246*1fa6dee9SAndroid Build Coastguard Worker case bool: 1247*1fa6dee9SAndroid Build Coastguard Worker return &parser.Bool{Value: v} 1248*1fa6dee9SAndroid Build Coastguard Worker case []string: 1249*1fa6dee9SAndroid Build Coastguard Worker values := make([]parser.Expression, 0, len(v)) 1250*1fa6dee9SAndroid Build Coastguard Worker for _, x := range v { 1251*1fa6dee9SAndroid Build Coastguard Worker values = append(values, &parser.String{Value: x}) 1252*1fa6dee9SAndroid Build Coastguard Worker } 1253*1fa6dee9SAndroid Build Coastguard Worker return &parser.List{Values: values} 1254*1fa6dee9SAndroid Build Coastguard Worker default: 1255*1fa6dee9SAndroid Build Coastguard Worker panic("unhandled type in configuredValueToExpression") 1256*1fa6dee9SAndroid Build Coastguard Worker } 1257*1fa6dee9SAndroid Build Coastguard Worker} 1258*1fa6dee9SAndroid Build Coastguard Worker 1259*1fa6dee9SAndroid Build Coastguard Workerfunc configuredTypeToString[T ConfigurableElements]() string { 1260*1fa6dee9SAndroid Build Coastguard Worker var zero T 1261*1fa6dee9SAndroid Build Coastguard Worker switch any(zero).(type) { 1262*1fa6dee9SAndroid Build Coastguard Worker case string: 1263*1fa6dee9SAndroid Build Coastguard Worker return "string" 1264*1fa6dee9SAndroid Build Coastguard Worker case bool: 1265*1fa6dee9SAndroid Build Coastguard Worker return "bool" 1266*1fa6dee9SAndroid Build Coastguard Worker case []string: 1267*1fa6dee9SAndroid Build Coastguard Worker return "list of strings" 1268*1fa6dee9SAndroid Build Coastguard Worker default: 1269*1fa6dee9SAndroid Build Coastguard Worker panic("should be unreachable") 1270*1fa6dee9SAndroid Build Coastguard Worker } 1271*1fa6dee9SAndroid Build Coastguard Worker} 1272*1fa6dee9SAndroid Build Coastguard Worker 1273*1fa6dee9SAndroid Build Coastguard Workerfunc copyConfiguredValue[T ConfigurableElements](t T) T { 1274*1fa6dee9SAndroid Build Coastguard Worker switch t2 := any(t).(type) { 1275*1fa6dee9SAndroid Build Coastguard Worker case []string: 1276*1fa6dee9SAndroid Build Coastguard Worker return any(slices.Clone(t2)).(T) 1277*1fa6dee9SAndroid Build Coastguard Worker default: 1278*1fa6dee9SAndroid Build Coastguard Worker return t 1279*1fa6dee9SAndroid Build Coastguard Worker } 1280*1fa6dee9SAndroid Build Coastguard Worker} 1281*1fa6dee9SAndroid Build Coastguard Worker 1282*1fa6dee9SAndroid Build Coastguard Workerfunc configuredValuePtrToOptional[T ConfigurableElements](t *T) ConfigurableOptional[T] { 1283*1fa6dee9SAndroid Build Coastguard Worker if t == nil { 1284*1fa6dee9SAndroid Build Coastguard Worker return ConfigurableOptional[T]{optional.NewShallowOptional(t)} 1285*1fa6dee9SAndroid Build Coastguard Worker } 1286*1fa6dee9SAndroid Build Coastguard Worker switch t2 := any(*t).(type) { 1287*1fa6dee9SAndroid Build Coastguard Worker case []string: 1288*1fa6dee9SAndroid Build Coastguard Worker result := any(slices.Clone(t2)).(T) 1289*1fa6dee9SAndroid Build Coastguard Worker return ConfigurableOptional[T]{optional.NewShallowOptional(&result)} 1290*1fa6dee9SAndroid Build Coastguard Worker default: 1291*1fa6dee9SAndroid Build Coastguard Worker return ConfigurableOptional[T]{optional.NewShallowOptional(t)} 1292*1fa6dee9SAndroid Build Coastguard Worker } 1293*1fa6dee9SAndroid Build Coastguard Worker} 1294*1fa6dee9SAndroid Build Coastguard Worker 1295*1fa6dee9SAndroid Build Coastguard Worker// PrintfIntoConfigurable replaces %s occurrences in strings in Configurable properties 1296*1fa6dee9SAndroid Build Coastguard Worker// with the provided string value. It's intention is to support soong config value variables 1297*1fa6dee9SAndroid Build Coastguard Worker// on Configurable properties. 1298*1fa6dee9SAndroid Build Coastguard Workerfunc PrintfIntoConfigurable(c any, value string) error { 1299*1fa6dee9SAndroid Build Coastguard Worker return c.(configurableReflection).printfInto(value) 1300*1fa6dee9SAndroid Build Coastguard Worker} 1301*1fa6dee9SAndroid Build Coastguard Worker 1302*1fa6dee9SAndroid Build Coastguard Workerfunc promoteValueToConfigurable(origional reflect.Value) reflect.Value { 1303*1fa6dee9SAndroid Build Coastguard Worker var expr parser.Expression 1304*1fa6dee9SAndroid Build Coastguard Worker var kind reflect.Kind 1305*1fa6dee9SAndroid Build Coastguard Worker if origional.Kind() == reflect.Pointer && origional.IsNil() { 1306*1fa6dee9SAndroid Build Coastguard Worker expr = &parser.UnsetProperty{} 1307*1fa6dee9SAndroid Build Coastguard Worker kind = origional.Type().Elem().Kind() 1308*1fa6dee9SAndroid Build Coastguard Worker } else { 1309*1fa6dee9SAndroid Build Coastguard Worker if origional.Kind() == reflect.Pointer { 1310*1fa6dee9SAndroid Build Coastguard Worker origional = origional.Elem() 1311*1fa6dee9SAndroid Build Coastguard Worker } 1312*1fa6dee9SAndroid Build Coastguard Worker kind = origional.Kind() 1313*1fa6dee9SAndroid Build Coastguard Worker switch kind { 1314*1fa6dee9SAndroid Build Coastguard Worker case reflect.String: 1315*1fa6dee9SAndroid Build Coastguard Worker expr = &parser.String{Value: origional.String()} 1316*1fa6dee9SAndroid Build Coastguard Worker case reflect.Bool: 1317*1fa6dee9SAndroid Build Coastguard Worker expr = &parser.Bool{Value: origional.Bool()} 1318*1fa6dee9SAndroid Build Coastguard Worker case reflect.Slice: 1319*1fa6dee9SAndroid Build Coastguard Worker strList := origional.Interface().([]string) 1320*1fa6dee9SAndroid Build Coastguard Worker exprList := make([]parser.Expression, 0, len(strList)) 1321*1fa6dee9SAndroid Build Coastguard Worker for _, x := range strList { 1322*1fa6dee9SAndroid Build Coastguard Worker exprList = append(exprList, &parser.String{Value: x}) 1323*1fa6dee9SAndroid Build Coastguard Worker } 1324*1fa6dee9SAndroid Build Coastguard Worker expr = &parser.List{Values: exprList} 1325*1fa6dee9SAndroid Build Coastguard Worker default: 1326*1fa6dee9SAndroid Build Coastguard Worker panic("can only convert string/bool/[]string to configurable") 1327*1fa6dee9SAndroid Build Coastguard Worker } 1328*1fa6dee9SAndroid Build Coastguard Worker } 1329*1fa6dee9SAndroid Build Coastguard Worker switch kind { 1330*1fa6dee9SAndroid Build Coastguard Worker case reflect.String: 1331*1fa6dee9SAndroid Build Coastguard Worker return reflect.ValueOf(Configurable[string]{ 1332*1fa6dee9SAndroid Build Coastguard Worker inner: &configurableInner[string]{ 1333*1fa6dee9SAndroid Build Coastguard Worker single: singleConfigurable[string]{ 1334*1fa6dee9SAndroid Build Coastguard Worker cases: []ConfigurableCase[string]{{ 1335*1fa6dee9SAndroid Build Coastguard Worker value: expr, 1336*1fa6dee9SAndroid Build Coastguard Worker }}, 1337*1fa6dee9SAndroid Build Coastguard Worker }, 1338*1fa6dee9SAndroid Build Coastguard Worker }, 1339*1fa6dee9SAndroid Build Coastguard Worker postProcessors: &[][]postProcessor[string]{}, 1340*1fa6dee9SAndroid Build Coastguard Worker }) 1341*1fa6dee9SAndroid Build Coastguard Worker case reflect.Bool: 1342*1fa6dee9SAndroid Build Coastguard Worker return reflect.ValueOf(Configurable[bool]{ 1343*1fa6dee9SAndroid Build Coastguard Worker inner: &configurableInner[bool]{ 1344*1fa6dee9SAndroid Build Coastguard Worker single: singleConfigurable[bool]{ 1345*1fa6dee9SAndroid Build Coastguard Worker cases: []ConfigurableCase[bool]{{ 1346*1fa6dee9SAndroid Build Coastguard Worker value: expr, 1347*1fa6dee9SAndroid Build Coastguard Worker }}, 1348*1fa6dee9SAndroid Build Coastguard Worker }, 1349*1fa6dee9SAndroid Build Coastguard Worker }, 1350*1fa6dee9SAndroid Build Coastguard Worker postProcessors: &[][]postProcessor[bool]{}, 1351*1fa6dee9SAndroid Build Coastguard Worker }) 1352*1fa6dee9SAndroid Build Coastguard Worker case reflect.Slice: 1353*1fa6dee9SAndroid Build Coastguard Worker return reflect.ValueOf(Configurable[[]string]{ 1354*1fa6dee9SAndroid Build Coastguard Worker inner: &configurableInner[[]string]{ 1355*1fa6dee9SAndroid Build Coastguard Worker single: singleConfigurable[[]string]{ 1356*1fa6dee9SAndroid Build Coastguard Worker cases: []ConfigurableCase[[]string]{{ 1357*1fa6dee9SAndroid Build Coastguard Worker value: expr, 1358*1fa6dee9SAndroid Build Coastguard Worker }}, 1359*1fa6dee9SAndroid Build Coastguard Worker }, 1360*1fa6dee9SAndroid Build Coastguard Worker }, 1361*1fa6dee9SAndroid Build Coastguard Worker postProcessors: &[][]postProcessor[[]string]{}, 1362*1fa6dee9SAndroid Build Coastguard Worker }) 1363*1fa6dee9SAndroid Build Coastguard Worker default: 1364*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Sprintf("Can't convert %s property to a configurable", origional.Kind().String())) 1365*1fa6dee9SAndroid Build Coastguard Worker } 1366*1fa6dee9SAndroid Build Coastguard Worker} 1367