blob: d037481f589d3a94bb856a13f098f82c153af74c [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 }
91
92 switch asgn.flavor {
93 case asgnSet:
94 emitAssignment()
95 case asgnAppend:
96 emitAppend()
97 case asgnMaybeAppend:
98 // If we are not sure variable has been assigned before, emit setdefault
99 if pcv.typ == starlarkTypeList {
100 gctx.writef("%s(handle, %q)", cfnSetListDefault, pcv.name())
101 } else {
102 gctx.writef("cfg.setdefault(%q, %s)", pcv.name(), pcv.defaultValueString())
103 }
104 gctx.newLine()
105 emitAppend()
106 case asgnMaybeSet:
107 gctx.writef("if cfg.get(%q) == None:", pcv.nam)
108 gctx.indentLevel++
109 gctx.newLine()
110 emitAssignment()
111 gctx.indentLevel--
112 }
113}
114
115func (pcv productConfigVariable) emitGet(gctx *generationContext, isDefined bool) {
116 if isDefined || pcv.isPreset() {
117 gctx.writef("cfg[%q]", pcv.nam)
118 } else {
119 gctx.writef("cfg.get(%q, %s)", pcv.nam, pcv.defaultValueString())
120 }
121}
122
123func (pcv productConfigVariable) emitDefined(gctx *generationContext) {
124 gctx.writef("g.get(%q) != None", pcv.name())
125}
126
127type otherGlobalVariable struct {
128 baseVariable
129}
130
131func (scv otherGlobalVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
132 emitAssignment := func() {
133 scv.emitGet(gctx, true)
134 gctx.write(" = ")
135 asgn.value.emitListVarCopy(gctx)
136 }
137
138 emitAppend := func() {
139 scv.emitGet(gctx, true)
140 gctx.write(" += ")
Cole Faust0484c232021-12-22 14:08:08 -0800141 value := asgn.value
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800142 if scv.valueType() == starlarkTypeString {
143 gctx.writef(`" " + `)
Cole Faust0484c232021-12-22 14:08:08 -0800144 value = &toStringExpr{expr: value}
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800145 }
Cole Faust0484c232021-12-22 14:08:08 -0800146 value.emit(gctx)
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800147 }
148
149 switch asgn.flavor {
150 case asgnSet:
151 emitAssignment()
152 case asgnAppend:
153 emitAppend()
154 case asgnMaybeAppend:
155 // If we are not sure variable has been assigned before, emit setdefault
156 gctx.writef("g.setdefault(%q, %s)", scv.name(), scv.defaultValueString())
157 gctx.newLine()
158 emitAppend()
159 case asgnMaybeSet:
160 gctx.writef("if g.get(%q) == None:", scv.nam)
161 gctx.indentLevel++
162 gctx.newLine()
163 emitAssignment()
164 gctx.indentLevel--
165 }
166}
167
168func (scv otherGlobalVariable) emitGet(gctx *generationContext, isDefined bool) {
169 if isDefined || scv.isPreset() {
170 gctx.writef("g[%q]", scv.nam)
171 } else {
172 gctx.writef("g.get(%q, %s)", scv.nam, scv.defaultValueString())
173 }
174}
175
176func (scv otherGlobalVariable) emitDefined(gctx *generationContext) {
177 gctx.writef("g.get(%q) != None", scv.name())
178}
179
180type localVariable struct {
181 baseVariable
182}
183
Sasha Smundakc4fa93e2021-11-05 14:38:46 -0700184func (lv localVariable) emitDefined(gctx *generationContext) {
185 gctx.writef(lv.String())
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800186}
187
188func (lv localVariable) String() string {
189 return "_" + lv.nam
190}
191
192func (lv localVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
193 switch asgn.flavor {
194 case asgnSet:
195 gctx.writef("%s = ", lv)
196 asgn.value.emitListVarCopy(gctx)
197 case asgnAppend:
198 lv.emitGet(gctx, false)
199 gctx.write(" += ")
Cole Faust0484c232021-12-22 14:08:08 -0800200 value := asgn.value
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800201 if lv.valueType() == starlarkTypeString {
202 gctx.writef(`" " + `)
Cole Faust0484c232021-12-22 14:08:08 -0800203 value = &toStringExpr{expr: value}
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800204 }
Cole Faust0484c232021-12-22 14:08:08 -0800205 value.emit(gctx)
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800206 case asgnMaybeAppend:
207 gctx.writef("%s(%q, ", cfnLocalAppend, lv)
208 asgn.value.emit(gctx)
209 gctx.write(")")
210 case asgnMaybeSet:
211 gctx.writef("%s(%q, ", cfnLocalSetDefault, lv)
212 asgn.value.emit(gctx)
213 gctx.write(")")
214 }
215}
216
217func (lv localVariable) emitGet(gctx *generationContext, _ bool) {
218 gctx.writef("%s", lv)
219}
220
221type predefinedVariable struct {
222 baseVariable
223 value starlarkExpr
224}
225
226func (pv predefinedVariable) emitGet(gctx *generationContext, _ bool) {
227 pv.value.emit(gctx)
228}
229
Sasha Smundak6609ba72021-07-22 18:32:56 -0700230func (pv predefinedVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800231 if expectedValue, ok1 := maybeString(pv.value); ok1 {
232 actualValue, ok2 := maybeString(asgn.value)
233 if ok2 {
234 if actualValue == expectedValue {
235 return
236 }
Sasha Smundak422b6142021-11-11 18:31:59 -0800237 gctx.emitConversionError(asgn.location,
238 fmt.Sprintf("cannot set predefined variable %s to %q, its value should be %q",
239 pv.name(), actualValue, expectedValue))
Sasha Smundak6609ba72021-07-22 18:32:56 -0700240 gctx.starScript.hasErrors = true
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800241 return
242 }
243 }
244 panic(fmt.Errorf("cannot set predefined variable %s to %q", pv.name(), asgn.mkValue.Dump()))
245}
246
247func (pv predefinedVariable) emitDefined(gctx *generationContext) {
248 gctx.write("True")
249}
250
251var localProductConfigVariables = map[string]string{
252 "LOCAL_AUDIO_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
253 "LOCAL_AUDIO_PRODUCT_COPY_FILES": "PRODUCT_COPY_FILES",
254 "LOCAL_AUDIO_DEVICE_PACKAGE_OVERLAYS": "DEVICE_PACKAGE_OVERLAYS",
255 "LOCAL_DUMPSTATE_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
256 "LOCAL_GATEKEEPER_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
257 "LOCAL_HEALTH_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
258 "LOCAL_SENSOR_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
259 "LOCAL_KEYMASTER_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
260 "LOCAL_KEYMINT_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
261}
262
263var presetVariables = map[string]bool{
264 "BUILD_ID": true,
265 "HOST_ARCH": true,
266 "HOST_OS": true,
267 "HOST_BUILD_TYPE": true,
268 "OUT_DIR": true,
269 "PLATFORM_VERSION_CODENAME": true,
270 "PLATFORM_VERSION": true,
271 "TARGET_ARCH": true,
272 "TARGET_ARCH_VARIANT": true,
273 "TARGET_BUILD_TYPE": true,
274 "TARGET_BUILD_VARIANT": true,
275 "TARGET_PRODUCT": true,
276}
277
278// addVariable returns a variable with a given name. A variable is
279// added if it does not exist yet.
280func (ctx *parseContext) addVariable(name string) variable {
Cole Faust3c4fc992022-02-28 16:05:01 -0800281 // Heuristics: if variable's name is all lowercase, consider it local
282 // string variable.
283 isLocalVariable := name == strings.ToLower(name)
284 // Local variables can't have special characters in them, because they
285 // will be used as starlark identifiers
286 if isLocalVariable {
287 name = strings.ReplaceAll(strings.TrimSpace(name), "-", "_")
288 }
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800289 v, found := ctx.variables[name]
290 if !found {
291 _, preset := presetVariables[name]
292 if vi, found := KnownVariables[name]; found {
293 switch vi.class {
294 case VarClassConfig:
295 v = &productConfigVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
296 case VarClassSoong:
297 v = &otherGlobalVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
298 }
Cole Faust3c4fc992022-02-28 16:05:01 -0800299 } else if isLocalVariable {
Sasha Smundak9d011ab2021-07-09 16:00:57 -0700300 v = &localVariable{baseVariable{nam: name, typ: starlarkTypeUnknown}}
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800301 } else {
302 vt := starlarkTypeUnknown
303 if strings.HasPrefix(name, "LOCAL_") {
304 // Heuristics: local variables that contribute to corresponding config variables
305 if cfgVarName, found := localProductConfigVariables[name]; found {
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 }
Sasha Smundak468e11f2021-08-26 09:10:23 -0700313 if strings.HasSuffix(name, "_LIST") && vt == starlarkTypeUnknown {
314 // Heuristics: Variables with "_LIST" suffix are lists
315 vt = starlarkTypeList
316 }
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800317 v = &otherGlobalVariable{baseVariable{nam: name, typ: vt}}
318 }
319 ctx.variables[name] = v
320 }
321 return v
322}