blob: 69297b94d70948b1120b1026190f3eed23c5b674 [file] [log] [blame]
Sasha Smundakb051c4e2020-11-05 20:45:07 -08001// 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"
Sasha Smundakb051c4e2020-11-05 20:45:07 -080019 "strings"
20)
21
22type variable interface {
23 name() string
24 emitGet(gctx *generationContext, isDefined bool)
25 emitSet(gctx *generationContext, asgn *assignmentNode)
26 emitDefined(gctx *generationContext)
27 valueType() starlarkType
Sasha Smundak9d011ab2021-07-09 16:00:57 -070028 setValueType(t starlarkType)
Sasha Smundakb051c4e2020-11-05 20:45:07 -080029 defaultValueString() string
30 isPreset() bool
31}
32
33type baseVariable struct {
34 nam string
35 typ starlarkType
36 preset bool // true if it has been initialized at startup
37}
38
39func (v baseVariable) name() string {
40 return v.nam
41}
42
43func (v baseVariable) valueType() starlarkType {
44 return v.typ
45}
46
Sasha Smundak9d011ab2021-07-09 16:00:57 -070047func (v *baseVariable) setValueType(t starlarkType) {
48 v.typ = t
49}
50
Sasha Smundakb051c4e2020-11-05 20:45:07 -080051func (v baseVariable) isPreset() bool {
52 return v.preset
53}
54
55var defaultValuesByType = map[starlarkType]string{
56 starlarkTypeUnknown: `""`,
57 starlarkTypeList: "[]",
58 starlarkTypeString: `""`,
59 starlarkTypeInt: "0",
60 starlarkTypeBool: "False",
61 starlarkTypeVoid: "None",
62}
63
64func (v baseVariable) defaultValueString() string {
65 if v, ok := defaultValuesByType[v.valueType()]; ok {
66 return v
67 }
68 panic(fmt.Errorf("%s has unknown type %q", v.name(), v.valueType()))
69}
70
71type productConfigVariable struct {
72 baseVariable
73}
74
75func (pcv productConfigVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
76 emitAssignment := func() {
77 pcv.emitGet(gctx, true)
78 gctx.write(" = ")
79 asgn.value.emitListVarCopy(gctx)
80 }
81 emitAppend := func() {
82 pcv.emitGet(gctx, true)
83 gctx.write(" += ")
Cole Faust0484c232021-12-22 14:08:08 -080084 value := asgn.value
Sasha Smundakb051c4e2020-11-05 20:45:07 -080085 if pcv.valueType() == starlarkTypeString {
86 gctx.writef(`" " + `)
Cole Faust0484c232021-12-22 14:08:08 -080087 value = &toStringExpr{expr: value}
Sasha Smundakb051c4e2020-11-05 20:45:07 -080088 }
Cole Faust0484c232021-12-22 14:08:08 -080089 value.emit(gctx)
Sasha Smundakb051c4e2020-11-05 20:45:07 -080090 }
Cole Faust816e0802022-03-04 12:04:31 -080091 emitSetDefault := func() {
Sasha Smundakb051c4e2020-11-05 20:45:07 -080092 if pcv.typ == starlarkTypeList {
93 gctx.writef("%s(handle, %q)", cfnSetListDefault, pcv.name())
94 } else {
95 gctx.writef("cfg.setdefault(%q, %s)", pcv.name(), pcv.defaultValueString())
96 }
97 gctx.newLine()
Cole Faust816e0802022-03-04 12:04:31 -080098 }
99
100 switch asgn.flavor {
101 case asgnSet:
102 isSelfReferential := false
103 asgn.value.transform(func(expr starlarkExpr) starlarkExpr {
104 if ref, ok := expr.(*variableRefExpr); ok && ref.ref.name() == pcv.name() {
105 isSelfReferential = true
106 }
107 return nil
108 })
109 if isSelfReferential {
110 emitSetDefault()
111 }
112 emitAssignment()
113 case asgnAppend:
114 emitAppend()
115 case asgnMaybeAppend:
116 // If we are not sure variable has been assigned before, emit setdefault
117 emitSetDefault()
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800118 emitAppend()
119 case asgnMaybeSet:
120 gctx.writef("if cfg.get(%q) == None:", pcv.nam)
121 gctx.indentLevel++
122 gctx.newLine()
123 emitAssignment()
124 gctx.indentLevel--
125 }
126}
127
128func (pcv productConfigVariable) emitGet(gctx *generationContext, isDefined bool) {
129 if isDefined || pcv.isPreset() {
130 gctx.writef("cfg[%q]", pcv.nam)
131 } else {
132 gctx.writef("cfg.get(%q, %s)", pcv.nam, pcv.defaultValueString())
133 }
134}
135
136func (pcv productConfigVariable) emitDefined(gctx *generationContext) {
137 gctx.writef("g.get(%q) != None", pcv.name())
138}
139
140type otherGlobalVariable struct {
141 baseVariable
142}
143
144func (scv otherGlobalVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
145 emitAssignment := func() {
146 scv.emitGet(gctx, true)
147 gctx.write(" = ")
148 asgn.value.emitListVarCopy(gctx)
149 }
150
151 emitAppend := func() {
152 scv.emitGet(gctx, true)
153 gctx.write(" += ")
Cole Faust0484c232021-12-22 14:08:08 -0800154 value := asgn.value
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800155 if scv.valueType() == starlarkTypeString {
156 gctx.writef(`" " + `)
Cole Faust0484c232021-12-22 14:08:08 -0800157 value = &toStringExpr{expr: value}
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800158 }
Cole Faust0484c232021-12-22 14:08:08 -0800159 value.emit(gctx)
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800160 }
161
162 switch asgn.flavor {
163 case asgnSet:
164 emitAssignment()
165 case asgnAppend:
166 emitAppend()
167 case asgnMaybeAppend:
168 // If we are not sure variable has been assigned before, emit setdefault
169 gctx.writef("g.setdefault(%q, %s)", scv.name(), scv.defaultValueString())
170 gctx.newLine()
171 emitAppend()
172 case asgnMaybeSet:
173 gctx.writef("if g.get(%q) == None:", scv.nam)
174 gctx.indentLevel++
175 gctx.newLine()
176 emitAssignment()
177 gctx.indentLevel--
178 }
179}
180
181func (scv otherGlobalVariable) emitGet(gctx *generationContext, isDefined bool) {
182 if isDefined || scv.isPreset() {
183 gctx.writef("g[%q]", scv.nam)
184 } else {
185 gctx.writef("g.get(%q, %s)", scv.nam, scv.defaultValueString())
186 }
187}
188
189func (scv otherGlobalVariable) emitDefined(gctx *generationContext) {
190 gctx.writef("g.get(%q) != None", scv.name())
191}
192
193type localVariable struct {
194 baseVariable
195}
196
Sasha Smundakc4fa93e2021-11-05 14:38:46 -0700197func (lv localVariable) emitDefined(gctx *generationContext) {
198 gctx.writef(lv.String())
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800199}
200
201func (lv localVariable) String() string {
202 return "_" + lv.nam
203}
204
205func (lv localVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
206 switch asgn.flavor {
207 case asgnSet:
208 gctx.writef("%s = ", lv)
209 asgn.value.emitListVarCopy(gctx)
210 case asgnAppend:
211 lv.emitGet(gctx, false)
212 gctx.write(" += ")
Cole Faust0484c232021-12-22 14:08:08 -0800213 value := asgn.value
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800214 if lv.valueType() == starlarkTypeString {
215 gctx.writef(`" " + `)
Cole Faust0484c232021-12-22 14:08:08 -0800216 value = &toStringExpr{expr: value}
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800217 }
Cole Faust0484c232021-12-22 14:08:08 -0800218 value.emit(gctx)
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800219 case asgnMaybeAppend:
220 gctx.writef("%s(%q, ", cfnLocalAppend, lv)
221 asgn.value.emit(gctx)
222 gctx.write(")")
223 case asgnMaybeSet:
224 gctx.writef("%s(%q, ", cfnLocalSetDefault, lv)
225 asgn.value.emit(gctx)
226 gctx.write(")")
227 }
228}
229
230func (lv localVariable) emitGet(gctx *generationContext, _ bool) {
231 gctx.writef("%s", lv)
232}
233
234type predefinedVariable struct {
235 baseVariable
236 value starlarkExpr
237}
238
239func (pv predefinedVariable) emitGet(gctx *generationContext, _ bool) {
240 pv.value.emit(gctx)
241}
242
Sasha Smundak6609ba72021-07-22 18:32:56 -0700243func (pv predefinedVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800244 if expectedValue, ok1 := maybeString(pv.value); ok1 {
245 actualValue, ok2 := maybeString(asgn.value)
246 if ok2 {
247 if actualValue == expectedValue {
248 return
249 }
Sasha Smundak422b6142021-11-11 18:31:59 -0800250 gctx.emitConversionError(asgn.location,
251 fmt.Sprintf("cannot set predefined variable %s to %q, its value should be %q",
252 pv.name(), actualValue, expectedValue))
Sasha Smundak6609ba72021-07-22 18:32:56 -0700253 gctx.starScript.hasErrors = true
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800254 return
255 }
256 }
257 panic(fmt.Errorf("cannot set predefined variable %s to %q", pv.name(), asgn.mkValue.Dump()))
258}
259
260func (pv predefinedVariable) emitDefined(gctx *generationContext) {
261 gctx.write("True")
262}
263
264var localProductConfigVariables = map[string]string{
265 "LOCAL_AUDIO_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
266 "LOCAL_AUDIO_PRODUCT_COPY_FILES": "PRODUCT_COPY_FILES",
267 "LOCAL_AUDIO_DEVICE_PACKAGE_OVERLAYS": "DEVICE_PACKAGE_OVERLAYS",
268 "LOCAL_DUMPSTATE_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
269 "LOCAL_GATEKEEPER_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
270 "LOCAL_HEALTH_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
271 "LOCAL_SENSOR_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
272 "LOCAL_KEYMASTER_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
273 "LOCAL_KEYMINT_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
274}
275
276var presetVariables = map[string]bool{
277 "BUILD_ID": true,
278 "HOST_ARCH": true,
279 "HOST_OS": true,
280 "HOST_BUILD_TYPE": true,
281 "OUT_DIR": true,
282 "PLATFORM_VERSION_CODENAME": true,
283 "PLATFORM_VERSION": true,
284 "TARGET_ARCH": true,
285 "TARGET_ARCH_VARIANT": true,
286 "TARGET_BUILD_TYPE": true,
287 "TARGET_BUILD_VARIANT": true,
288 "TARGET_PRODUCT": true,
289}
290
291// addVariable returns a variable with a given name. A variable is
292// added if it does not exist yet.
293func (ctx *parseContext) addVariable(name string) variable {
Cole Faustf92c9f22022-03-14 14:35:50 -0700294 // Get the hintType before potentially changing the variable name
295 var hintType starlarkType
296 var ok bool
297 if hintType, ok = ctx.typeHints[name]; !ok {
298 hintType = starlarkTypeUnknown
299 }
Cole Faust3c4fc992022-02-28 16:05:01 -0800300 // Heuristics: if variable's name is all lowercase, consider it local
301 // string variable.
302 isLocalVariable := name == strings.ToLower(name)
303 // Local variables can't have special characters in them, because they
304 // will be used as starlark identifiers
305 if isLocalVariable {
306 name = strings.ReplaceAll(strings.TrimSpace(name), "-", "_")
307 }
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800308 v, found := ctx.variables[name]
309 if !found {
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800310 if vi, found := KnownVariables[name]; found {
Cole Faustf92c9f22022-03-14 14:35:50 -0700311 _, preset := presetVariables[name]
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800312 switch vi.class {
313 case VarClassConfig:
314 v = &productConfigVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
315 case VarClassSoong:
316 v = &otherGlobalVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
317 }
Cole Faust3c4fc992022-02-28 16:05:01 -0800318 } else if isLocalVariable {
Cole Faustf92c9f22022-03-14 14:35:50 -0700319 v = &localVariable{baseVariable{nam: name, typ: hintType}}
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800320 } else {
Cole Faustf92c9f22022-03-14 14:35:50 -0700321 vt := hintType
322 if strings.HasPrefix(name, "LOCAL_") && vt == starlarkTypeUnknown {
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800323 // Heuristics: local variables that contribute to corresponding config variables
324 if cfgVarName, found := localProductConfigVariables[name]; found {
325 vi, found2 := KnownVariables[cfgVarName]
326 if !found2 {
327 panic(fmt.Errorf("unknown config variable %s for %s", cfgVarName, name))
328 }
329 vt = vi.valueType
330 }
331 }
Sasha Smundak468e11f2021-08-26 09:10:23 -0700332 if strings.HasSuffix(name, "_LIST") && vt == starlarkTypeUnknown {
333 // Heuristics: Variables with "_LIST" suffix are lists
334 vt = starlarkTypeList
335 }
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800336 v = &otherGlobalVariable{baseVariable{nam: name, typ: vt}}
337 }
338 ctx.variables[name] = v
339 }
340 return v
341}