2020-11-06 05:45:07 +01:00
// 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 )
}
}
2021-07-23 03:32:56 +02:00
type moduleInfo struct {
2020-11-06 05:45:07 +01:00
path string // Converted Starlark file path
originalPath string // Makefile file path
moduleLocalName string
2021-07-23 03:32:56 +02:00
optional bool
2022-01-11 02:02:16 +01:00
missing bool // a module may not exist if a module that depends on it is loaded dynamically
2020-11-06 05:45:07 +01:00
}
2021-07-23 03:32:56 +02:00
func ( im moduleInfo ) entryName ( ) string {
return im . moduleLocalName + "_init"
2020-11-06 05:45:07 +01:00
}
2022-01-18 19:31:14 +01:00
func ( mi moduleInfo ) name ( ) string {
return fmt . Sprintf ( "%q" , MakePath2ModuleName ( mi . originalPath ) )
}
2021-07-23 03:32:56 +02:00
type inheritedModule interface {
name ( ) string
entryName ( ) string
emitSelect ( gctx * generationContext )
2022-01-11 02:02:16 +01:00
pathExpr ( ) starlarkExpr
needsLoadCheck ( ) bool
2021-07-23 03:32:56 +02:00
}
type inheritedStaticModule struct {
* moduleInfo
loadAlways bool
}
func ( im inheritedStaticModule ) emitSelect ( _ * generationContext ) {
}
2022-01-11 02:02:16 +01:00
func ( im inheritedStaticModule ) pathExpr ( ) starlarkExpr {
return & stringLiteralExpr { im . path }
}
func ( im inheritedStaticModule ) needsLoadCheck ( ) bool {
return im . missing
2021-07-23 03:32:56 +02:00
}
type inheritedDynamicModule struct {
2022-04-27 01:27:22 +02:00
path starlarkExpr
2021-07-23 03:32:56 +02:00
candidateModules [ ] * moduleInfo
loadAlways bool
2022-01-27 02:47:33 +01:00
location ErrorLocation
needsWarning bool
2021-07-23 03:32:56 +02:00
}
func ( i inheritedDynamicModule ) name ( ) string {
return "_varmod"
}
func ( i inheritedDynamicModule ) entryName ( ) string {
return i . name ( ) + "_init"
}
func ( i inheritedDynamicModule ) emitSelect ( gctx * generationContext ) {
2022-01-27 02:47:33 +01:00
if i . needsWarning {
gctx . newLine ( )
2022-02-08 21:49:37 +01:00
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." )
2022-01-27 02:47:33 +01:00
}
2021-07-23 03:32:56 +02:00
gctx . newLine ( )
gctx . writef ( "_entry = {" )
gctx . indentLevel ++
for _ , mi := range i . candidateModules {
gctx . newLine ( )
2022-01-18 19:31:14 +01:00
gctx . writef ( ` "%s": (%s, %s), ` , mi . originalPath , mi . name ( ) , mi . entryName ( ) )
2021-07-23 03:32:56 +02:00
}
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 ( ) )
}
2022-01-11 02:02:16 +01:00
func ( i inheritedDynamicModule ) pathExpr ( ) starlarkExpr {
2022-04-27 01:27:22 +02:00
return i . path
2022-01-11 02:02:16 +01:00
}
func ( i inheritedDynamicModule ) needsLoadCheck ( ) bool {
return true
2020-11-06 05:45:07 +01:00
}
type inheritNode struct {
2021-09-24 01:20:58 +02:00
module inheritedModule
loadAlways bool
2020-11-06 05:45:07 +01:00
}
func ( inn * inheritNode ) emit ( gctx * generationContext ) {
// Unconditional case:
2022-01-11 02:02:16 +01:00
// maybe check that loaded
2020-11-06 05:45:07 +01:00
// rblf.inherit(handle, <module>, module_init)
// Conditional case:
// if <module>_init != None:
// same as above
2021-07-23 03:32:56 +02:00
inn . module . emitSelect ( gctx )
name := inn . module . name ( )
entry := inn . module . entryName ( )
2021-09-24 01:20:58 +02:00
if inn . loadAlways {
2022-01-11 02:02:16 +01:00
gctx . emitLoadCheck ( inn . module )
gctx . newLine ( )
2021-07-23 03:32:56 +02:00
gctx . writef ( "%s(handle, %s, %s)" , cfnInherit , name , entry )
2020-11-06 05:45:07 +01:00
return
}
2021-07-23 03:32:56 +02:00
2022-01-11 02:02:16 +01:00
gctx . newLine ( )
2021-07-23 03:32:56 +02:00
gctx . writef ( "if %s:" , entry )
2020-11-06 05:45:07 +01:00
gctx . indentLevel ++
gctx . newLine ( )
2021-07-23 03:32:56 +02:00
gctx . writef ( "%s(handle, %s, %s)" , cfnInherit , name , entry )
2020-11-06 05:45:07 +01:00
gctx . indentLevel --
}
type includeNode struct {
2021-09-24 01:20:58 +02:00
module inheritedModule
loadAlways bool
2020-11-06 05:45:07 +01:00
}
func ( inn * includeNode ) emit ( gctx * generationContext ) {
2021-07-23 03:32:56 +02:00
inn . module . emitSelect ( gctx )
entry := inn . module . entryName ( )
2021-09-24 01:20:58 +02:00
if inn . loadAlways {
2022-01-11 02:02:16 +01:00
gctx . emitLoadCheck ( inn . module )
gctx . newLine ( )
2021-07-23 03:32:56 +02:00
gctx . writef ( "%s(g, handle)" , entry )
2020-11-06 05:45:07 +01:00
return
}
2021-07-23 03:32:56 +02:00
2022-01-11 02:02:16 +01:00
gctx . newLine ( )
2021-07-23 03:32:56 +02:00
gctx . writef ( "if %s != None:" , entry )
2020-11-06 05:45:07 +01:00
gctx . indentLevel ++
gctx . newLine ( )
2021-07-23 03:32:56 +02:00
gctx . writef ( "%s(g, handle)" , entry )
2020-11-06 05:45:07 +01:00
gctx . indentLevel --
}
type assignmentFlavor int
const (
// Assignment flavors
2022-03-10 01:00:17 +01:00
asgnSet assignmentFlavor = iota // := or =
asgnMaybeSet assignmentFlavor = iota // ?=
asgnAppend assignmentFlavor = iota // +=
2020-11-06 05:45:07 +01:00
)
type assignmentNode struct {
lhs variable
value starlarkExpr
mkValue * mkparser . MakeString
flavor assignmentFlavor
2021-11-12 03:31:59 +01:00
location ErrorLocation
2020-11-06 05:45:07 +01:00
isTraced bool
}
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 ( ) )
2022-04-07 22:59:24 +02:00
asgn . lhs . emitGet ( gctx )
2020-11-06 05:45:07 +01:00
gctx . writef ( ")" )
}
}
2022-03-10 01:00:17 +01:00
func ( asgn * assignmentNode ) isSelfReferential ( ) bool {
if asgn . flavor == asgnAppend {
return true
}
isSelfReferential := false
asgn . value . transform ( func ( expr starlarkExpr ) starlarkExpr {
if ref , ok := expr . ( * variableRefExpr ) ; ok && ref . ref . name ( ) == asgn . lhs . name ( ) {
isSelfReferential = true
}
return nil
} )
return isSelfReferential
}
2020-11-06 05:45:07 +01:00
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 ++
2022-04-07 22:59:24 +02:00
gctx . pushVariableAssignments ( )
2020-11-06 05:45:07 +01:00
hasStatements := false
2022-02-01 00:48:29 +01:00
for _ , node := range cb . nodes {
2020-11-06 05:45:07 +01:00
if _ , ok := node . ( * commentNode ) ; ! ok {
hasStatements = true
}
node . emit ( gctx )
}
2022-02-01 00:48:29 +01:00
if ! hasStatements {
2020-11-06 05:45:07 +01:00
gctx . emitPass ( )
}
gctx . indentLevel --
2022-04-07 22:59:24 +02:00
gctx . popVariableAssignments ( )
2020-11-06 05:45:07 +01:00
}
// A single complete if ... elseif ... else ... endif sequences
type switchNode struct {
ssCases [ ] * switchCase
}
func ( ssw * switchNode ) emit ( gctx * generationContext ) {
2022-02-01 00:48:29 +01:00
for _ , ssCase := range ssw . ssCases {
ssCase . emit ( gctx )
2020-11-06 05:45:07 +01:00
}
}
2022-03-28 23:02:50 +02:00
type foreachNode struct {
varName string
list starlarkExpr
actions [ ] starlarkNode
}
func ( f * foreachNode ) emit ( gctx * generationContext ) {
2022-04-07 22:59:24 +02:00
gctx . pushVariableAssignments ( )
2022-03-28 23:02:50 +02:00
gctx . newLine ( )
gctx . writef ( "for %s in " , f . varName )
f . list . emit ( gctx )
gctx . write ( ":" )
gctx . indentLevel ++
hasStatements := false
for _ , a := range f . actions {
if _ , ok := a . ( * commentNode ) ; ! ok {
hasStatements = true
}
a . emit ( gctx )
}
if ! hasStatements {
gctx . emitPass ( )
}
gctx . indentLevel --
2022-04-07 22:59:24 +02:00
gctx . popVariableAssignments ( )
2022-03-28 23:02:50 +02:00
}