f4e72cf642
As requested on aosp/1975486 Fixes: 217248902 Test: Presubmits Change-Id: I14e65722d8ab96125827d34e8501b4a518a27b57
283 lines
6.5 KiB
Go
283 lines
6.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 moduleInfo struct {
|
|
path string // Converted Starlark file path
|
|
originalPath string // Makefile file path
|
|
moduleLocalName string
|
|
optional bool
|
|
missing bool // a module may not exist if a module that depends on it is loaded dynamically
|
|
}
|
|
|
|
func (im moduleInfo) entryName() string {
|
|
return im.moduleLocalName + "_init"
|
|
}
|
|
|
|
func (mi moduleInfo) name() string {
|
|
return fmt.Sprintf("%q", MakePath2ModuleName(mi.originalPath))
|
|
}
|
|
|
|
type inheritedModule interface {
|
|
name() string
|
|
entryName() string
|
|
emitSelect(gctx *generationContext)
|
|
pathExpr() starlarkExpr
|
|
needsLoadCheck() bool
|
|
}
|
|
|
|
type inheritedStaticModule struct {
|
|
*moduleInfo
|
|
loadAlways bool
|
|
}
|
|
|
|
func (im inheritedStaticModule) emitSelect(_ *generationContext) {
|
|
}
|
|
|
|
func (im inheritedStaticModule) pathExpr() starlarkExpr {
|
|
return &stringLiteralExpr{im.path}
|
|
}
|
|
|
|
func (im inheritedStaticModule) needsLoadCheck() bool {
|
|
return im.missing
|
|
}
|
|
|
|
type inheritedDynamicModule struct {
|
|
path interpolateExpr
|
|
candidateModules []*moduleInfo
|
|
loadAlways bool
|
|
location ErrorLocation
|
|
needsWarning bool
|
|
}
|
|
|
|
func (i inheritedDynamicModule) name() string {
|
|
return "_varmod"
|
|
}
|
|
|
|
func (i inheritedDynamicModule) entryName() string {
|
|
return i.name() + "_init"
|
|
}
|
|
|
|
func (i inheritedDynamicModule) emitSelect(gctx *generationContext) {
|
|
if i.needsWarning {
|
|
gctx.newLine()
|
|
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.")
|
|
}
|
|
gctx.newLine()
|
|
gctx.writef("_entry = {")
|
|
gctx.indentLevel++
|
|
for _, mi := range i.candidateModules {
|
|
gctx.newLine()
|
|
gctx.writef(`"%s": (%s, %s),`, mi.originalPath, mi.name(), 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())
|
|
}
|
|
|
|
func (i inheritedDynamicModule) pathExpr() starlarkExpr {
|
|
return &i.path
|
|
}
|
|
|
|
func (i inheritedDynamicModule) needsLoadCheck() bool {
|
|
return true
|
|
}
|
|
|
|
type inheritNode struct {
|
|
module inheritedModule
|
|
loadAlways bool
|
|
}
|
|
|
|
func (inn *inheritNode) emit(gctx *generationContext) {
|
|
// Unconditional case:
|
|
// maybe check that loaded
|
|
// 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()
|
|
if inn.loadAlways {
|
|
gctx.emitLoadCheck(inn.module)
|
|
gctx.newLine()
|
|
gctx.writef("%s(handle, %s, %s)", cfnInherit, name, entry)
|
|
return
|
|
}
|
|
|
|
gctx.newLine()
|
|
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()
|
|
if inn.loadAlways {
|
|
gctx.emitLoadCheck(inn.module)
|
|
gctx.newLine()
|
|
gctx.writef("%s(g, handle)", entry)
|
|
return
|
|
}
|
|
|
|
gctx.newLine()
|
|
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) emit(gctx *generationContext) {
|
|
cb.gate.emit(gctx)
|
|
gctx.indentLevel++
|
|
hasStatements := false
|
|
for _, node := range cb.nodes {
|
|
if _, ok := node.(*commentNode); !ok {
|
|
hasStatements = true
|
|
}
|
|
node.emit(gctx)
|
|
}
|
|
if !hasStatements {
|
|
gctx.emitPass()
|
|
}
|
|
gctx.indentLevel--
|
|
}
|
|
|
|
// A single complete if ... elseif ... else ... endif sequences
|
|
type switchNode struct {
|
|
ssCases []*switchCase
|
|
}
|
|
|
|
func (ssw *switchNode) emit(gctx *generationContext) {
|
|
for _, ssCase := range ssw.ssCases {
|
|
ssCase.emit(gctx)
|
|
}
|
|
}
|