Test: treehugger; internal tests in mk2rbc_test.go Bug: 172923994 Change-Id: I43120b9c181ef2b8d9453e743233811b0fec268b
238 lines
5.5 KiB
Go
238 lines
5.5 KiB
Go
// 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 inheritedModule struct {
|
|
path string // Converted Starlark file path
|
|
originalPath string // Makefile file path
|
|
moduleName string
|
|
moduleLocalName string
|
|
loadAlways bool
|
|
}
|
|
|
|
func (im inheritedModule) name() string {
|
|
return MakePath2ModuleName(im.originalPath)
|
|
}
|
|
|
|
func (im inheritedModule) entryName() string {
|
|
return im.moduleLocalName + "_init"
|
|
}
|
|
|
|
type inheritNode struct {
|
|
*inheritedModule
|
|
}
|
|
|
|
func (inn *inheritNode) emit(gctx *generationContext) {
|
|
// Unconditional case:
|
|
// rblf.inherit(handle, <module>, module_init)
|
|
// Conditional case:
|
|
// if <module>_init != None:
|
|
// same as above
|
|
gctx.newLine()
|
|
if inn.loadAlways {
|
|
gctx.writef("%s(handle, %q, %s)", cfnInherit, inn.name(), inn.entryName())
|
|
return
|
|
}
|
|
gctx.writef("if %s != None:", inn.entryName())
|
|
gctx.indentLevel++
|
|
gctx.newLine()
|
|
gctx.writef("%s(handle, %q, %s)", cfnInherit, inn.name(), inn.entryName())
|
|
gctx.indentLevel--
|
|
}
|
|
|
|
type includeNode struct {
|
|
*inheritedModule
|
|
}
|
|
|
|
func (inn *includeNode) emit(gctx *generationContext) {
|
|
gctx.newLine()
|
|
if inn.loadAlways {
|
|
gctx.writef("%s(g, handle)", inn.entryName())
|
|
return
|
|
}
|
|
gctx.writef("if %s != None:", inn.entryName())
|
|
gctx.indentLevel++
|
|
gctx.newLine()
|
|
gctx.writef("%s(g, handle)", inn.entryName())
|
|
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
|
|
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()
|
|
if bad, ok := in.expr.(*badExpr); ok {
|
|
gctx.write("# MK2STAR ERROR converting:")
|
|
gctx.newLine()
|
|
gctx.writef("# %s", bad.node.Dump())
|
|
gctx.newLine()
|
|
gctx.writef("# %s", bad.message)
|
|
gctx.newLine()
|
|
// The init function emits a warning if the conversion was not
|
|
// fullly successful, so here we (arbitrarily) take the false path.
|
|
gctx.writef("%sFalse:", ifElif)
|
|
return
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
}
|