• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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