Support if expressions in mk2rbc
Bug: 201700692
Test: go test
Change-Id: Icdf30c4d625d81974db946bd91660a29a0373ac7
diff --git a/mk2rbc/expr.go b/mk2rbc/expr.go
index ec0b279..81b31c7 100644
--- a/mk2rbc/expr.go
+++ b/mk2rbc/expr.go
@@ -85,6 +85,31 @@
s.emit(gctx)
}
+// Boolean literal
+type boolLiteralExpr struct {
+ literal bool
+}
+
+func (b *boolLiteralExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
+ return b, true
+}
+
+func (b *boolLiteralExpr) emit(gctx *generationContext) {
+ if b.literal {
+ gctx.write("True")
+ } else {
+ gctx.write("False")
+ }
+}
+
+func (_ *boolLiteralExpr) typ() starlarkType {
+ return starlarkTypeBool
+}
+
+func (b *boolLiteralExpr) emitListVarCopy(gctx *generationContext) {
+ b.emit(gctx)
+}
+
// interpolateExpr represents Starlark's interpolation operator <string> % list
// we break <string> into a list of chunks, i.e., "first%second%third" % (X, Y)
// will have chunks = ["first", "second", "third"] and args = [X, Y]
@@ -617,6 +642,55 @@
cx.emit(gctx)
}
+type ifExpr struct {
+ condition starlarkExpr
+ ifTrue starlarkExpr
+ ifFalse starlarkExpr
+}
+
+func (i *ifExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
+ cond, condSame := i.condition.eval(valueMap)
+ t, tSame := i.ifTrue.eval(valueMap)
+ f, fSame := i.ifFalse.eval(valueMap)
+ same = condSame && tSame && fSame
+ if same {
+ return i, same
+ } else {
+ return &ifExpr{
+ condition: cond,
+ ifTrue: t,
+ ifFalse: f,
+ }, same
+ }
+}
+
+func (i *ifExpr) emit(gctx *generationContext) {
+ gctx.write("(")
+ i.ifTrue.emit(gctx)
+ gctx.write(" if ")
+ i.condition.emit(gctx)
+ gctx.write(" else ")
+ i.ifFalse.emit(gctx)
+ gctx.write(")")
+}
+
+func (i *ifExpr) typ() starlarkType {
+ tType := i.ifTrue.typ()
+ fType := i.ifFalse.typ()
+ if tType != fType && tType != starlarkTypeUnknown && fType != starlarkTypeUnknown {
+ panic("Conflicting types in if expression")
+ }
+ if tType != starlarkTypeUnknown {
+ return tType
+ } else {
+ return fType
+ }
+}
+
+func (i *ifExpr) emitListVarCopy(gctx *generationContext) {
+ i.emit(gctx)
+}
+
type badExpr struct {
errorLocation ErrorLocation
message string
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index cade4d2..d1140fa 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -112,6 +112,7 @@
"filter-out": {baseName + ".filter_out", starlarkTypeList, hiddenArgNone},
"firstword": {"!firstword", starlarkTypeString, hiddenArgNone},
"get-vendor-board-platforms": {"!get-vendor-board-platforms", starlarkTypeList, hiddenArgNone}, // internal macro, used by is-board-platform, etc.
+ "if": {"!if", starlarkTypeUnknown, hiddenArgNone},
"info": {baseName + ".mkinfo", starlarkTypeVoid, hiddenArgNone},
"is-android-codename": {"!is-android-codename", starlarkTypeBool, hiddenArgNone}, // unused by product config
"is-android-codename-in-list": {"!is-android-codename-in-list", starlarkTypeBool, hiddenArgNone}, // unused by product config
@@ -1368,6 +1369,8 @@
return ctx.newBadExpr(node, "cannot handle invoking %s", expr.name)
}
switch expr.name {
+ case "if":
+ return ctx.parseIfFunc(node, args)
case "word":
return ctx.parseWordFunc(node, args)
case "firstword", "lastword":
@@ -1423,6 +1426,35 @@
}
}
+func (ctx *parseContext) parseIfFunc(node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+ words := args.Split(",")
+ if len(words) != 2 && len(words) != 3 {
+ return ctx.newBadExpr(node, "if function should have 2 or 3 arguments, found "+strconv.Itoa(len(words)))
+ }
+ condition := ctx.parseMakeString(node, words[0])
+ ifTrue := ctx.parseMakeString(node, words[1])
+ var ifFalse starlarkExpr
+ if len(words) == 3 {
+ ifFalse = ctx.parseMakeString(node, words[2])
+ } else {
+ switch ifTrue.typ() {
+ case starlarkTypeList:
+ ifFalse = &listExpr{items: []starlarkExpr{}}
+ case starlarkTypeInt:
+ ifFalse = &intLiteralExpr{literal: 0}
+ case starlarkTypeBool:
+ ifFalse = &boolLiteralExpr{literal: false}
+ default:
+ ifFalse = &stringLiteralExpr{literal: ""}
+ }
+ }
+ return &ifExpr{
+ condition,
+ ifTrue,
+ ifFalse,
+ }
+}
+
func (ctx *parseContext) parseWordFunc(node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
words := args.Split(",")
if len(words) != 2 {
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index fa33e75..0048ad1 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -1091,6 +1091,29 @@
pass
`,
},
+ {
+ desc: "if expression",
+ mkname: "product.mk",
+ in: `
+TEST_VAR := foo
+TEST_VAR_LIST := foo
+TEST_VAR_LIST += bar
+TEST_VAR_2 := $(if $(TEST_VAR),bar)
+TEST_VAR_3 := $(if $(TEST_VAR),bar,baz)
+TEST_VAR_3 := $(if $(TEST_VAR),$(TEST_VAR_LIST))
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ g["TEST_VAR"] = "foo"
+ g["TEST_VAR_LIST"] = ["foo"]
+ g["TEST_VAR_LIST"] += ["bar"]
+ g["TEST_VAR_2"] = ("bar" if g["TEST_VAR"] else "")
+ g["TEST_VAR_3"] = ("bar" if g["TEST_VAR"] else "baz")
+ g["TEST_VAR_3"] = (g["TEST_VAR_LIST"] if g["TEST_VAR"] else [])
+`,
+ },
}
var known_variables = []struct {