6609ba7664
Bug: 193566316 Test: internal Change-Id: Iaa7b68cf459f9a694ae9d37a32c9372cf8a8335a
306 lines
6.9 KiB
Go
306 lines
6.9 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 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)
|
|
isLoadAlways() 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) isLoadAlways() 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("cannot")`)
|
|
gctx.indentLevel--
|
|
}
|
|
}
|
|
|
|
func (i inheritedDynamicModule) isLoadAlways() bool {
|
|
return i.loadAlways
|
|
}
|
|
|
|
type inheritNode struct {
|
|
module inheritedModule
|
|
}
|
|
|
|
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.module.isLoadAlways() {
|
|
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
|
|
}
|
|
|
|
func (inn *includeNode) emit(gctx *generationContext) {
|
|
inn.module.emitSelect(gctx)
|
|
entry := inn.module.entryName()
|
|
gctx.newLine()
|
|
if inn.module.isLoadAlways() {
|
|
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
|
|
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)
|
|
}
|
|
}
|
|
}
|