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