blob: a650453ead2c164bc378d8bc09199226e9470079 [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"
19 "os"
20 "strings"
21)
22
23type variable interface {
24 name() string
25 emitGet(gctx *generationContext, isDefined bool)
26 emitSet(gctx *generationContext, asgn *assignmentNode)
27 emitDefined(gctx *generationContext)
28 valueType() starlarkType
Sasha Smundak9d011ab2021-07-09 16:00:57 -070029 setValueType(t starlarkType)
Sasha Smundakb051c4e2020-11-05 20:45:07 -080030 defaultValueString() string
31 isPreset() bool
32}
33
34type baseVariable struct {
35 nam string
36 typ starlarkType
37 preset bool // true if it has been initialized at startup
38}
39
40func (v baseVariable) name() string {
41 return v.nam
42}
43
44func (v baseVariable) valueType() starlarkType {
45 return v.typ
46}
47
Sasha Smundak9d011ab2021-07-09 16:00:57 -070048func (v *baseVariable) setValueType(t starlarkType) {
49 v.typ = t
50}
51
Sasha Smundakb051c4e2020-11-05 20:45:07 -080052func (v baseVariable) isPreset() bool {
53 return v.preset
54}
55
56var defaultValuesByType = map[starlarkType]string{
57 starlarkTypeUnknown: `""`,
58 starlarkTypeList: "[]",
59 starlarkTypeString: `""`,
60 starlarkTypeInt: "0",
61 starlarkTypeBool: "False",
62 starlarkTypeVoid: "None",
63}
64
65func (v baseVariable) defaultValueString() string {
66 if v, ok := defaultValuesByType[v.valueType()]; ok {
67 return v
68 }
69 panic(fmt.Errorf("%s has unknown type %q", v.name(), v.valueType()))
70}
71
72type productConfigVariable struct {
73 baseVariable
74}
75
76func (pcv productConfigVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
77 emitAssignment := func() {
78 pcv.emitGet(gctx, true)
79 gctx.write(" = ")
80 asgn.value.emitListVarCopy(gctx)
81 }
82 emitAppend := func() {
83 pcv.emitGet(gctx, true)
84 gctx.write(" += ")
85 if pcv.valueType() == starlarkTypeString {
86 gctx.writef(`" " + `)
87 }
88 asgn.value.emit(gctx)
89 }
90
91 switch asgn.flavor {
92 case asgnSet:
93 emitAssignment()
94 case asgnAppend:
95 emitAppend()
96 case asgnMaybeAppend:
97 // If we are not sure variable has been assigned before, emit setdefault
98 if pcv.typ == starlarkTypeList {
99 gctx.writef("%s(handle, %q)", cfnSetListDefault, pcv.name())
100 } else {
101 gctx.writef("cfg.setdefault(%q, %s)", pcv.name(), pcv.defaultValueString())
102 }
103 gctx.newLine()
104 emitAppend()
105 case asgnMaybeSet:
106 gctx.writef("if cfg.get(%q) == None:", pcv.nam)
107 gctx.indentLevel++
108 gctx.newLine()
109 emitAssignment()
110 gctx.indentLevel--
111 }
112}
113
114func (pcv productConfigVariable) emitGet(gctx *generationContext, isDefined bool) {
115 if isDefined || pcv.isPreset() {
116 gctx.writef("cfg[%q]", pcv.nam)
117 } else {
118 gctx.writef("cfg.get(%q, %s)", pcv.nam, pcv.defaultValueString())
119 }
120}
121
122func (pcv productConfigVariable) emitDefined(gctx *generationContext) {
123 gctx.writef("g.get(%q) != None", pcv.name())
124}
125
126type otherGlobalVariable struct {
127 baseVariable
128}
129
130func (scv otherGlobalVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
131 emitAssignment := func() {
132 scv.emitGet(gctx, true)
133 gctx.write(" = ")
134 asgn.value.emitListVarCopy(gctx)
135 }
136
137 emitAppend := func() {
138 scv.emitGet(gctx, true)
139 gctx.write(" += ")
140 if scv.valueType() == starlarkTypeString {
141 gctx.writef(`" " + `)
142 }
143 asgn.value.emit(gctx)
144 }
145
146 switch asgn.flavor {
147 case asgnSet:
148 emitAssignment()
149 case asgnAppend:
150 emitAppend()
151 case asgnMaybeAppend:
152 // If we are not sure variable has been assigned before, emit setdefault
153 gctx.writef("g.setdefault(%q, %s)", scv.name(), scv.defaultValueString())
154 gctx.newLine()
155 emitAppend()
156 case asgnMaybeSet:
157 gctx.writef("if g.get(%q) == None:", scv.nam)
158 gctx.indentLevel++
159 gctx.newLine()
160 emitAssignment()
161 gctx.indentLevel--
162 }
163}
164
165func (scv otherGlobalVariable) emitGet(gctx *generationContext, isDefined bool) {
166 if isDefined || scv.isPreset() {
167 gctx.writef("g[%q]", scv.nam)
168 } else {
169 gctx.writef("g.get(%q, %s)", scv.nam, scv.defaultValueString())
170 }
171}
172
173func (scv otherGlobalVariable) emitDefined(gctx *generationContext) {
174 gctx.writef("g.get(%q) != None", scv.name())
175}
176
177type localVariable struct {
178 baseVariable
179}
180
181func (lv localVariable) emitDefined(_ *generationContext) {
182 panic("implement me")
183}
184
185func (lv localVariable) String() string {
186 return "_" + lv.nam
187}
188
189func (lv localVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
190 switch asgn.flavor {
191 case asgnSet:
192 gctx.writef("%s = ", lv)
193 asgn.value.emitListVarCopy(gctx)
194 case asgnAppend:
195 lv.emitGet(gctx, false)
196 gctx.write(" += ")
197 if lv.valueType() == starlarkTypeString {
198 gctx.writef(`" " + `)
199 }
200 asgn.value.emit(gctx)
201 case asgnMaybeAppend:
202 gctx.writef("%s(%q, ", cfnLocalAppend, lv)
203 asgn.value.emit(gctx)
204 gctx.write(")")
205 case asgnMaybeSet:
206 gctx.writef("%s(%q, ", cfnLocalSetDefault, lv)
207 asgn.value.emit(gctx)
208 gctx.write(")")
209 }
210}
211
212func (lv localVariable) emitGet(gctx *generationContext, _ bool) {
213 gctx.writef("%s", lv)
214}
215
216type predefinedVariable struct {
217 baseVariable
218 value starlarkExpr
219}
220
221func (pv predefinedVariable) emitGet(gctx *generationContext, _ bool) {
222 pv.value.emit(gctx)
223}
224
225func (pv predefinedVariable) emitSet(_ *generationContext, asgn *assignmentNode) {
226 if expectedValue, ok1 := maybeString(pv.value); ok1 {
227 actualValue, ok2 := maybeString(asgn.value)
228 if ok2 {
229 if actualValue == expectedValue {
230 return
231 }
232 fmt.Fprintf(os.Stderr, "cannot set predefined variable %s to %q, its value should be %q",
233 pv.name(), actualValue, expectedValue)
234 return
235 }
236 }
237 panic(fmt.Errorf("cannot set predefined variable %s to %q", pv.name(), asgn.mkValue.Dump()))
238}
239
240func (pv predefinedVariable) emitDefined(gctx *generationContext) {
241 gctx.write("True")
242}
243
244var localProductConfigVariables = map[string]string{
245 "LOCAL_AUDIO_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
246 "LOCAL_AUDIO_PRODUCT_COPY_FILES": "PRODUCT_COPY_FILES",
247 "LOCAL_AUDIO_DEVICE_PACKAGE_OVERLAYS": "DEVICE_PACKAGE_OVERLAYS",
248 "LOCAL_DUMPSTATE_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
249 "LOCAL_GATEKEEPER_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
250 "LOCAL_HEALTH_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
251 "LOCAL_SENSOR_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
252 "LOCAL_KEYMASTER_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
253 "LOCAL_KEYMINT_PRODUCT_PACKAGE": "PRODUCT_PACKAGES",
254}
255
256var presetVariables = map[string]bool{
257 "BUILD_ID": true,
258 "HOST_ARCH": true,
259 "HOST_OS": true,
260 "HOST_BUILD_TYPE": true,
261 "OUT_DIR": true,
262 "PLATFORM_VERSION_CODENAME": true,
263 "PLATFORM_VERSION": true,
264 "TARGET_ARCH": true,
265 "TARGET_ARCH_VARIANT": true,
266 "TARGET_BUILD_TYPE": true,
267 "TARGET_BUILD_VARIANT": true,
268 "TARGET_PRODUCT": true,
269}
270
271// addVariable returns a variable with a given name. A variable is
272// added if it does not exist yet.
273func (ctx *parseContext) addVariable(name string) variable {
274 v, found := ctx.variables[name]
275 if !found {
276 _, preset := presetVariables[name]
277 if vi, found := KnownVariables[name]; found {
278 switch vi.class {
279 case VarClassConfig:
280 v = &productConfigVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
281 case VarClassSoong:
282 v = &otherGlobalVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
283 }
284 } else if name == strings.ToLower(name) {
285 // Heuristics: if variable's name is all lowercase, consider it local
286 // string variable.
Sasha Smundak9d011ab2021-07-09 16:00:57 -0700287 v = &localVariable{baseVariable{nam: name, typ: starlarkTypeUnknown}}
Sasha Smundakb051c4e2020-11-05 20:45:07 -0800288 } else {
289 vt := starlarkTypeUnknown
290 if strings.HasPrefix(name, "LOCAL_") {
291 // Heuristics: local variables that contribute to corresponding config variables
292 if cfgVarName, found := localProductConfigVariables[name]; found {
293 vi, found2 := KnownVariables[cfgVarName]
294 if !found2 {
295 panic(fmt.Errorf("unknown config variable %s for %s", cfgVarName, name))
296 }
297 vt = vi.valueType
298 }
299 }
300 v = &otherGlobalVariable{baseVariable{nam: name, typ: vt}}
301 }
302 ctx.variables[name] = v
303 }
304 return v
305}