1// Copyright 2021 Google LLC 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package mk2rbc 16 17import ( 18 "fmt" 19 "strings" 20) 21 22type variable interface { 23 name() string 24 emitGet(gctx *generationContext) 25 emitSet(gctx *generationContext, asgn *assignmentNode) 26 valueType() starlarkType 27 setValueType(t starlarkType) 28 defaultValueString() string 29 isPreset() bool 30} 31 32type baseVariable struct { 33 nam string 34 typ starlarkType 35 preset bool // true if it has been initialized at startup 36} 37 38func (v baseVariable) name() string { 39 return v.nam 40} 41 42func (v baseVariable) valueType() starlarkType { 43 return v.typ 44} 45 46func (v *baseVariable) setValueType(t starlarkType) { 47 v.typ = t 48} 49 50func (v baseVariable) isPreset() bool { 51 return v.preset 52} 53 54var defaultValuesByType = map[starlarkType]string{ 55 starlarkTypeUnknown: `""`, 56 starlarkTypeList: "[]", 57 starlarkTypeString: `""`, 58 starlarkTypeInt: "0", 59 starlarkTypeBool: "False", 60 starlarkTypeVoid: "None", 61} 62 63func (v baseVariable) defaultValueString() string { 64 if v, ok := defaultValuesByType[v.valueType()]; ok { 65 return v 66 } 67 panic(fmt.Errorf("%s has unknown type %q", v.name(), v.valueType())) 68} 69 70type productConfigVariable struct { 71 baseVariable 72} 73 74func (pcv productConfigVariable) emitSet(gctx *generationContext, asgn *assignmentNode) { 75 emitAssignment := func() { 76 gctx.writef("cfg[%q] = ", pcv.nam) 77 asgn.value.emitListVarCopy(gctx) 78 } 79 emitAppend := func() { 80 gctx.writef("cfg[%q] += ", pcv.nam) 81 value := asgn.value 82 if pcv.valueType() == starlarkTypeString { 83 gctx.writef(`" " + `) 84 value = &toStringExpr{expr: value} 85 } 86 value.emit(gctx) 87 } 88 emitSetDefault := func() { 89 if pcv.typ == starlarkTypeList { 90 gctx.writef("%s(handle, %q)", cfnSetListDefault, pcv.name()) 91 } else { 92 gctx.writef("cfg.setdefault(%q, %s)", pcv.name(), pcv.defaultValueString()) 93 } 94 gctx.newLine() 95 } 96 97 // If we are not sure variable has been assigned before, emit setdefault 98 needsSetDefault := !gctx.hasBeenAssigned(&pcv) && !pcv.isPreset() && asgn.isSelfReferential() 99 100 switch asgn.flavor { 101 case asgnSet: 102 if needsSetDefault { 103 emitSetDefault() 104 } 105 emitAssignment() 106 case asgnAppend: 107 if needsSetDefault { 108 emitSetDefault() 109 } 110 emitAppend() 111 case asgnMaybeSet: 112 gctx.writef("if cfg.get(%q) == None:", pcv.nam) 113 gctx.indentLevel++ 114 gctx.newLine() 115 if needsSetDefault { 116 emitSetDefault() 117 } 118 emitAssignment() 119 gctx.indentLevel-- 120 } 121 122 gctx.setHasBeenAssigned(&pcv) 123} 124 125func (pcv productConfigVariable) emitGet(gctx *generationContext) { 126 if gctx.hasBeenAssigned(&pcv) || pcv.isPreset() { 127 gctx.writef("cfg[%q]", pcv.nam) 128 } else { 129 gctx.writef("cfg.get(%q, %s)", pcv.nam, pcv.defaultValueString()) 130 } 131} 132 133type otherGlobalVariable struct { 134 baseVariable 135} 136 137func (scv otherGlobalVariable) emitSet(gctx *generationContext, asgn *assignmentNode) { 138 emitAssignment := func() { 139 gctx.writef("g[%q] = ", scv.nam) 140 asgn.value.emitListVarCopy(gctx) 141 } 142 143 emitAppend := func() { 144 gctx.writef("g[%q] += ", scv.nam) 145 value := asgn.value 146 if scv.valueType() == starlarkTypeString { 147 gctx.writef(`" " + `) 148 value = &toStringExpr{expr: value} 149 } 150 value.emit(gctx) 151 } 152 153 // If we are not sure variable has been assigned before, emit setdefault 154 needsSetDefault := !gctx.hasBeenAssigned(&scv) && !scv.isPreset() && asgn.isSelfReferential() 155 156 switch asgn.flavor { 157 case asgnSet: 158 if needsSetDefault { 159 gctx.writef("g.setdefault(%q, %s)", scv.name(), scv.defaultValueString()) 160 gctx.newLine() 161 } 162 emitAssignment() 163 case asgnAppend: 164 if needsSetDefault { 165 gctx.writef("g.setdefault(%q, %s)", scv.name(), scv.defaultValueString()) 166 gctx.newLine() 167 } 168 emitAppend() 169 case asgnMaybeSet: 170 gctx.writef("if g.get(%q) == None:", scv.nam) 171 gctx.indentLevel++ 172 gctx.newLine() 173 if needsSetDefault { 174 gctx.writef("g.setdefault(%q, %s)", scv.name(), scv.defaultValueString()) 175 gctx.newLine() 176 } 177 emitAssignment() 178 gctx.indentLevel-- 179 } 180 181 gctx.setHasBeenAssigned(&scv) 182} 183 184func (scv otherGlobalVariable) emitGet(gctx *generationContext) { 185 if gctx.hasBeenAssigned(&scv) || scv.isPreset() { 186 gctx.writef("g[%q]", scv.nam) 187 } else { 188 gctx.writef("g.get(%q, %s)", scv.nam, scv.defaultValueString()) 189 } 190} 191 192type localVariable struct { 193 baseVariable 194} 195 196func (lv localVariable) String() string { 197 return "_" + lv.nam 198} 199 200func (lv localVariable) emitSet(gctx *generationContext, asgn *assignmentNode) { 201 switch asgn.flavor { 202 case asgnSet, asgnMaybeSet: 203 gctx.writef("%s = ", lv) 204 asgn.value.emitListVarCopy(gctx) 205 case asgnAppend: 206 gctx.writef("%s += ", lv) 207 value := asgn.value 208 if lv.valueType() == starlarkTypeString { 209 gctx.writef(`" " + `) 210 value = &toStringExpr{expr: value} 211 } 212 value.emit(gctx) 213 } 214} 215 216func (lv localVariable) emitGet(gctx *generationContext) { 217 gctx.writef("%s", lv) 218} 219 220type predefinedVariable struct { 221 baseVariable 222 value starlarkExpr 223} 224 225func (pv predefinedVariable) emitGet(gctx *generationContext) { 226 pv.value.emit(gctx) 227} 228 229func (pv predefinedVariable) emitSet(gctx *generationContext, asgn *assignmentNode) { 230 if expectedValue, ok1 := maybeString(pv.value); ok1 { 231 actualValue, ok2 := maybeString(asgn.value) 232 if ok2 { 233 if actualValue == expectedValue { 234 return 235 } 236 gctx.emitConversionError(asgn.location, 237 fmt.Sprintf("cannot set predefined variable %s to %q, its value should be %q", 238 pv.name(), actualValue, expectedValue)) 239 gctx.starScript.hasErrors = true 240 return 241 } 242 } 243 panic(fmt.Errorf("cannot set predefined variable %s to %q", pv.name(), asgn.mkValue.Dump())) 244} 245 246var localProductConfigVariables = map[string]string{ 247 "LOCAL_AUDIO_PRODUCT_PACKAGE": "PRODUCT_PACKAGES", 248 "LOCAL_AUDIO_PRODUCT_COPY_FILES": "PRODUCT_COPY_FILES", 249 "LOCAL_AUDIO_DEVICE_PACKAGE_OVERLAYS": "DEVICE_PACKAGE_OVERLAYS", 250 "LOCAL_DUMPSTATE_PRODUCT_PACKAGE": "PRODUCT_PACKAGES", 251 "LOCAL_GATEKEEPER_PRODUCT_PACKAGE": "PRODUCT_PACKAGES", 252 "LOCAL_HEALTH_PRODUCT_PACKAGE": "PRODUCT_PACKAGES", 253 "LOCAL_SENSOR_PRODUCT_PACKAGE": "PRODUCT_PACKAGES", 254 "LOCAL_KEYMASTER_PRODUCT_PACKAGE": "PRODUCT_PACKAGES", 255 "LOCAL_KEYMINT_PRODUCT_PACKAGE": "PRODUCT_PACKAGES", 256} 257 258var presetVariables = map[string]bool{ 259 "BUILD_ID": true, 260 "HOST_ARCH": true, 261 "HOST_OS": true, 262 "HOST_BUILD_TYPE": true, 263 "OUT_DIR": true, 264 "PLATFORM_VERSION_CODENAME": true, 265 "PLATFORM_VERSION": true, 266 "TARGET_ARCH": true, 267 "TARGET_ARCH_VARIANT": true, 268 "TARGET_BUILD_TYPE": true, 269 "TARGET_BUILD_VARIANT": true, 270 "TARGET_PRODUCT": true, 271} 272 273// addVariable returns a variable with a given name. A variable is 274// added if it does not exist yet. 275func (ctx *parseContext) addVariable(name string) variable { 276 // Get the hintType before potentially changing the variable name 277 var hintType starlarkType 278 var ok bool 279 if hintType, ok = ctx.typeHints[name]; !ok { 280 hintType = starlarkTypeUnknown 281 } 282 // Heuristics: if variable's name is all lowercase, consider it local 283 // string variable. 284 isLocalVariable := name == strings.ToLower(name) 285 // Local variables can't have special characters in them, because they 286 // will be used as starlark identifiers 287 if isLocalVariable { 288 name = strings.ReplaceAll(strings.TrimSpace(name), "-", "_") 289 } 290 v, found := ctx.variables[name] 291 if !found { 292 if vi, found := KnownVariables[name]; found { 293 _, preset := presetVariables[name] 294 switch vi.class { 295 case VarClassConfig: 296 v = &productConfigVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}} 297 case VarClassSoong: 298 v = &otherGlobalVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}} 299 } 300 } else if isLocalVariable { 301 v = &localVariable{baseVariable{nam: name, typ: hintType}} 302 } else { 303 vt := hintType 304 // Heuristics: local variables that contribute to corresponding config variables 305 if cfgVarName, found := localProductConfigVariables[name]; found && vt == starlarkTypeUnknown { 306 vi, found2 := KnownVariables[cfgVarName] 307 if !found2 { 308 panic(fmt.Errorf("unknown config variable %s for %s", cfgVarName, name)) 309 } 310 vt = vi.valueType 311 } 312 if strings.HasSuffix(name, "_LIST") && vt == starlarkTypeUnknown { 313 // Heuristics: Variables with "_LIST" suffix are lists 314 vt = starlarkTypeList 315 } 316 v = &otherGlobalVariable{baseVariable{nam: name, typ: vt}} 317 } 318 ctx.variables[name] = v 319 } 320 return v 321} 322