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