• 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	mkparser "android/soong/androidmk/parser"
22)
23
24// A parsed node for which starlark code will be generated
25// by calling emit().
26type starlarkNode interface {
27	emit(ctx *generationContext)
28}
29
30// Types used to keep processed makefile data:
31type commentNode struct {
32	text string
33}
34
35func (c *commentNode) emit(gctx *generationContext) {
36	chunks := strings.Split(c.text, "\\\n")
37	gctx.newLine()
38	gctx.write(chunks[0]) // It has '#' at the beginning already.
39	for _, chunk := range chunks[1:] {
40		gctx.newLine()
41		gctx.write("#", chunk)
42	}
43}
44
45type moduleInfo struct {
46	path            string // Converted Starlark file path
47	originalPath    string // Makefile file path
48	moduleLocalName string
49	optional        bool
50	missing         bool // a module may not exist if a module that depends on it is loaded dynamically
51}
52
53func (im moduleInfo) entryName() string {
54	return im.moduleLocalName + "_init"
55}
56
57func (mi moduleInfo) name() string {
58	return fmt.Sprintf("%q", MakePath2ModuleName(mi.originalPath))
59}
60
61type inheritedModule interface {
62	name() string
63	entryName() string
64	emitSelect(gctx *generationContext)
65	pathExpr() starlarkExpr
66	needsLoadCheck() bool
67}
68
69type inheritedStaticModule struct {
70	*moduleInfo
71	loadAlways bool
72}
73
74func (im inheritedStaticModule) emitSelect(_ *generationContext) {
75}
76
77func (im inheritedStaticModule) pathExpr() starlarkExpr {
78	return &stringLiteralExpr{im.path}
79}
80
81func (im inheritedStaticModule) needsLoadCheck() bool {
82	return im.missing
83}
84
85type inheritedDynamicModule struct {
86	path             interpolateExpr
87	candidateModules []*moduleInfo
88	loadAlways       bool
89	location         ErrorLocation
90	needsWarning     bool
91}
92
93func (i inheritedDynamicModule) name() string {
94	return "_varmod"
95}
96
97func (i inheritedDynamicModule) entryName() string {
98	return i.name() + "_init"
99}
100
101func (i inheritedDynamicModule) emitSelect(gctx *generationContext) {
102	if i.needsWarning {
103		gctx.newLine()
104		gctx.writef("%s.mkwarning(%q, %q)", baseName, i.location, "Please avoid starting an include path with a variable. See https://source.android.com/setup/build/bazel/product_config/issues/includes for details.")
105	}
106	gctx.newLine()
107	gctx.writef("_entry = {")
108	gctx.indentLevel++
109	for _, mi := range i.candidateModules {
110		gctx.newLine()
111		gctx.writef(`"%s": (%s, %s),`, mi.originalPath, mi.name(), mi.entryName())
112	}
113	gctx.indentLevel--
114	gctx.newLine()
115	gctx.write("}.get(")
116	i.path.emit(gctx)
117	gctx.write(")")
118	gctx.newLine()
119	gctx.writef("(%s, %s) = _entry if _entry else (None, None)", i.name(), i.entryName())
120}
121
122func (i inheritedDynamicModule) pathExpr() starlarkExpr {
123	return &i.path
124}
125
126func (i inheritedDynamicModule) needsLoadCheck() bool {
127	return true
128}
129
130type inheritNode struct {
131	module     inheritedModule
132	loadAlways bool
133}
134
135func (inn *inheritNode) emit(gctx *generationContext) {
136	// Unconditional case:
137	//    maybe check that loaded
138	//    rblf.inherit(handle, <module>, module_init)
139	// Conditional case:
140	//    if <module>_init != None:
141	//      same as above
142	inn.module.emitSelect(gctx)
143	name := inn.module.name()
144	entry := inn.module.entryName()
145	if inn.loadAlways {
146		gctx.emitLoadCheck(inn.module)
147		gctx.newLine()
148		gctx.writef("%s(handle, %s, %s)", cfnInherit, name, entry)
149		return
150	}
151
152	gctx.newLine()
153	gctx.writef("if %s:", entry)
154	gctx.indentLevel++
155	gctx.newLine()
156	gctx.writef("%s(handle, %s, %s)", cfnInherit, name, entry)
157	gctx.indentLevel--
158}
159
160type includeNode struct {
161	module     inheritedModule
162	loadAlways bool
163}
164
165func (inn *includeNode) emit(gctx *generationContext) {
166	inn.module.emitSelect(gctx)
167	entry := inn.module.entryName()
168	if inn.loadAlways {
169		gctx.emitLoadCheck(inn.module)
170		gctx.newLine()
171		gctx.writef("%s(g, handle)", entry)
172		return
173	}
174
175	gctx.newLine()
176	gctx.writef("if %s != None:", entry)
177	gctx.indentLevel++
178	gctx.newLine()
179	gctx.writef("%s(g, handle)", entry)
180	gctx.indentLevel--
181}
182
183type assignmentFlavor int
184
185const (
186	// Assignment flavors
187	asgnSet      assignmentFlavor = iota // := or =
188	asgnMaybeSet assignmentFlavor = iota // ?=
189	asgnAppend   assignmentFlavor = iota // +=
190)
191
192type assignmentNode struct {
193	lhs      variable
194	value    starlarkExpr
195	mkValue  *mkparser.MakeString
196	flavor   assignmentFlavor
197	location ErrorLocation
198	isTraced bool
199}
200
201func (asgn *assignmentNode) emit(gctx *generationContext) {
202	gctx.newLine()
203	gctx.inAssignment = true
204	asgn.lhs.emitSet(gctx, asgn)
205	gctx.inAssignment = false
206
207	if asgn.isTraced {
208		gctx.newLine()
209		gctx.tracedCount++
210		gctx.writef(`print("%s.%d: %s := ", `, gctx.starScript.mkFile, gctx.tracedCount, asgn.lhs.name())
211		asgn.lhs.emitGet(gctx)
212		gctx.writef(")")
213	}
214}
215
216func (asgn *assignmentNode) isSelfReferential() bool {
217	if asgn.flavor == asgnAppend {
218		return true
219	}
220	isSelfReferential := false
221	asgn.value.transform(func(expr starlarkExpr) starlarkExpr {
222		if ref, ok := expr.(*variableRefExpr); ok && ref.ref.name() == asgn.lhs.name() {
223			isSelfReferential = true
224		}
225		return nil
226	})
227	return isSelfReferential
228}
229
230type exprNode struct {
231	expr starlarkExpr
232}
233
234func (exn *exprNode) emit(gctx *generationContext) {
235	gctx.newLine()
236	exn.expr.emit(gctx)
237}
238
239type ifNode struct {
240	isElif bool // true if this is 'elif' statement
241	expr   starlarkExpr
242}
243
244func (in *ifNode) emit(gctx *generationContext) {
245	ifElif := "if "
246	if in.isElif {
247		ifElif = "elif "
248	}
249
250	gctx.newLine()
251	gctx.write(ifElif)
252	in.expr.emit(gctx)
253	gctx.write(":")
254}
255
256type elseNode struct{}
257
258func (br *elseNode) emit(gctx *generationContext) {
259	gctx.newLine()
260	gctx.write("else:")
261}
262
263// switchCase represents as single if/elseif/else branch. All the necessary
264// info about flavor (if/elseif/else) is supposed to be kept in `gate`.
265type switchCase struct {
266	gate  starlarkNode
267	nodes []starlarkNode
268}
269
270func (cb *switchCase) emit(gctx *generationContext) {
271	cb.gate.emit(gctx)
272	gctx.indentLevel++
273	gctx.pushVariableAssignments()
274	hasStatements := false
275	for _, node := range cb.nodes {
276		if _, ok := node.(*commentNode); !ok {
277			hasStatements = true
278		}
279		node.emit(gctx)
280	}
281	if !hasStatements {
282		gctx.emitPass()
283	}
284	gctx.indentLevel--
285	gctx.popVariableAssignments()
286}
287
288// A single complete if ... elseif ... else ... endif sequences
289type switchNode struct {
290	ssCases []*switchCase
291}
292
293func (ssw *switchNode) emit(gctx *generationContext) {
294	for _, ssCase := range ssw.ssCases {
295		ssCase.emit(gctx)
296	}
297}
298
299type foreachNode struct {
300	varName string
301	list    starlarkExpr
302	actions []starlarkNode
303}
304
305func (f *foreachNode) emit(gctx *generationContext) {
306	gctx.pushVariableAssignments()
307	gctx.newLine()
308	gctx.writef("for %s in ", f.varName)
309	f.list.emit(gctx)
310	gctx.write(":")
311	gctx.indentLevel++
312	hasStatements := false
313	for _, a := range f.actions {
314		if _, ok := a.(*commentNode); !ok {
315			hasStatements = true
316		}
317		a.emit(gctx)
318	}
319	if !hasStatements {
320		gctx.emitPass()
321	}
322	gctx.indentLevel--
323	gctx.popVariableAssignments()
324}
325