blob: ebc57b23b412caf879aed8c791f5ef7d4ad5aafe [file] [log] [blame]
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package mk2rbc
import (
"fmt"
"strings"
mkparser "android/soong/androidmk/parser"
)
// A parsed node for which starlark code will be generated
// by calling emit().
type starlarkNode interface {
emit(ctx *generationContext)
}
// Types used to keep processed makefile data:
type commentNode struct {
text string
}
func (c *commentNode) emit(gctx *generationContext) {
chunks := strings.Split(c.text, "\\\n")
gctx.newLine()
gctx.write(chunks[0]) // It has '#' at the beginning already.
for _, chunk := range chunks[1:] {
gctx.newLine()
gctx.write("#", chunk)
}
}
type moduleInfo struct {
path string // Converted Starlark file path
originalPath string // Makefile file path
moduleLocalName string
optional bool
}
func (im moduleInfo) entryName() string {
return im.moduleLocalName + "_init"
}
type inheritedModule interface {
name() string
entryName() string
emitSelect(gctx *generationContext)
shouldExist() bool
}
type inheritedStaticModule struct {
*moduleInfo
loadAlways bool
}
func (im inheritedStaticModule) name() string {
return fmt.Sprintf("%q", MakePath2ModuleName(im.originalPath))
}
func (im inheritedStaticModule) emitSelect(_ *generationContext) {
}
func (im inheritedStaticModule) shouldExist() bool {
return im.loadAlways
}
type inheritedDynamicModule struct {
path interpolateExpr
candidateModules []*moduleInfo
loadAlways bool
}
func (i inheritedDynamicModule) name() string {
return "_varmod"
}
func (i inheritedDynamicModule) entryName() string {
return i.name() + "_init"
}
func (i inheritedDynamicModule) emitSelect(gctx *generationContext) {
gctx.newLine()
gctx.writef("_entry = {")
gctx.indentLevel++
for _, mi := range i.candidateModules {
gctx.newLine()
gctx.writef(`"%s": (%q, %s),`, mi.originalPath, mi.moduleLocalName, mi.entryName())
}
gctx.indentLevel--
gctx.newLine()
gctx.write("}.get(")
i.path.emit(gctx)
gctx.write(")")
gctx.newLine()
gctx.writef("(%s, %s) = _entry if _entry else (None, None)", i.name(), i.entryName())
if i.loadAlways {
gctx.newLine()
gctx.writef("if not %s:", i.entryName())
gctx.indentLevel++
gctx.newLine()
gctx.write(`rblf.mkerror("`, gctx.starScript.mkFile, `", "Cannot find %s" % (`)
i.path.emit(gctx)
gctx.write("))")
gctx.indentLevel--
}
}
func (i inheritedDynamicModule) shouldExist() bool {
return i.loadAlways
}
type inheritNode struct {
module inheritedModule
loadAlways bool
}
func (inn *inheritNode) emit(gctx *generationContext) {
// Unconditional case:
// rblf.inherit(handle, <module>, module_init)
// Conditional case:
// if <module>_init != None:
// same as above
inn.module.emitSelect(gctx)
name := inn.module.name()
entry := inn.module.entryName()
gctx.newLine()
if inn.loadAlways {
gctx.writef("%s(handle, %s, %s)", cfnInherit, name, entry)
return
}
gctx.writef("if %s:", entry)
gctx.indentLevel++
gctx.newLine()
gctx.writef("%s(handle, %s, %s)", cfnInherit, name, entry)
gctx.indentLevel--
}
type includeNode struct {
module inheritedModule
loadAlways bool
}
func (inn *includeNode) emit(gctx *generationContext) {
inn.module.emitSelect(gctx)
entry := inn.module.entryName()
gctx.newLine()
if inn.loadAlways {
gctx.writef("%s(g, handle)", entry)
return
}
gctx.writef("if %s != None:", entry)
gctx.indentLevel++
gctx.newLine()
gctx.writef("%s(g, handle)", entry)
gctx.indentLevel--
}
type assignmentFlavor int
const (
// Assignment flavors
asgnSet assignmentFlavor = iota // := or =
asgnMaybeSet assignmentFlavor = iota // ?= and variable may be unset
asgnAppend assignmentFlavor = iota // += and variable has been set before
asgnMaybeAppend assignmentFlavor = iota // += and variable may be unset
)
type assignmentNode struct {
lhs variable
value starlarkExpr
mkValue *mkparser.MakeString
flavor assignmentFlavor
location ErrorLocation
isTraced bool
previous *assignmentNode
}
func (asgn *assignmentNode) emit(gctx *generationContext) {
gctx.newLine()
gctx.inAssignment = true
asgn.lhs.emitSet(gctx, asgn)
gctx.inAssignment = false
if asgn.isTraced {
gctx.newLine()
gctx.tracedCount++
gctx.writef(`print("%s.%d: %s := ", `, gctx.starScript.mkFile, gctx.tracedCount, asgn.lhs.name())
asgn.lhs.emitGet(gctx, true)
gctx.writef(")")
}
}
type exprNode struct {
expr starlarkExpr
}
func (exn *exprNode) emit(gctx *generationContext) {
gctx.newLine()
exn.expr.emit(gctx)
}
type ifNode struct {
isElif bool // true if this is 'elif' statement
expr starlarkExpr
}
func (in *ifNode) emit(gctx *generationContext) {
ifElif := "if "
if in.isElif {
ifElif = "elif "
}
gctx.newLine()
gctx.write(ifElif)
in.expr.emit(gctx)
gctx.write(":")
}
type elseNode struct{}
func (br *elseNode) emit(gctx *generationContext) {
gctx.newLine()
gctx.write("else:")
}
// switchCase represents as single if/elseif/else branch. All the necessary
// info about flavor (if/elseif/else) is supposed to be kept in `gate`.
type switchCase struct {
gate starlarkNode
nodes []starlarkNode
}
func (cb *switchCase) newNode(node starlarkNode) {
cb.nodes = append(cb.nodes, node)
}
func (cb *switchCase) emit(gctx *generationContext) {
cb.gate.emit(gctx)
gctx.indentLevel++
hasStatements := false
emitNode := func(node starlarkNode) {
if _, ok := node.(*commentNode); !ok {
hasStatements = true
}
node.emit(gctx)
}
if len(cb.nodes) > 0 {
emitNode(cb.nodes[0])
for _, node := range cb.nodes[1:] {
emitNode(node)
}
if !hasStatements {
gctx.emitPass()
}
} else {
gctx.emitPass()
}
gctx.indentLevel--
}
// A single complete if ... elseif ... else ... endif sequences
type switchNode struct {
ssCases []*switchCase
}
func (ssw *switchNode) newNode(node starlarkNode) {
switch br := node.(type) {
case *switchCase:
ssw.ssCases = append(ssw.ssCases, br)
default:
panic(fmt.Errorf("expected switchCase node, got %t", br))
}
}
func (ssw *switchNode) emit(gctx *generationContext) {
if len(ssw.ssCases) == 0 {
gctx.emitPass()
} else {
ssw.ssCases[0].emit(gctx)
for _, ssCase := range ssw.ssCases[1:] {
ssCase.emit(gctx)
}
}
}