Product config makefiles to Starlark converter
Test: treehugger; internal tests in mk2rbc_test.go Bug: 172923994 Change-Id: I43120b9c181ef2b8d9453e743233811b0fec268b
This commit is contained in:
237
mk2rbc/node.go
Normal file
237
mk2rbc/node.go
Normal file
@@ -0,0 +1,237 @@
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user