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