Merge "Allow generic if statements" am: 6d0500d556

Original change: https://android-review.googlesource.com/c/platform/build/soong/+/1889720

Change-Id: I68ead331b44ff56758be868a5d1d455dff6081b8
This commit is contained in:
Cole Faust
2021-11-15 23:42:27 +00:00
committed by Automerger Merge Worker
3 changed files with 204 additions and 124 deletions

View File

@@ -197,6 +197,51 @@ func (v *variableRefExpr) emitListVarCopy(gctx *generationContext) {
}
}
type toStringExpr struct {
expr starlarkExpr
}
func (s *toStringExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
if x, same := s.expr.eval(valueMap); same {
res = s
} else {
res = &toStringExpr{expr: x}
}
return
}
func (s *toStringExpr) emit(ctx *generationContext) {
switch s.expr.typ() {
case starlarkTypeString, starlarkTypeUnknown:
// Assume unknown types are strings already.
s.expr.emit(ctx)
case starlarkTypeList:
ctx.write(`" ".join(`)
s.expr.emit(ctx)
ctx.write(")")
case starlarkTypeInt:
ctx.write(`("%d" % (`)
s.expr.emit(ctx)
ctx.write("))")
case starlarkTypeBool:
ctx.write("((")
s.expr.emit(ctx)
ctx.write(`) ? "true" : "")`)
case starlarkTypeVoid:
ctx.write(`""`)
default:
panic("Unknown starlark type!")
}
}
func (s *toStringExpr) typ() starlarkType {
return starlarkTypeString
}
func (s *toStringExpr) emitListVarCopy(gctx *generationContext) {
s.emit(gctx)
}
type notExpr struct {
expr starlarkExpr
}
@@ -255,6 +300,12 @@ func (eq *eqExpr) emit(gctx *generationContext) {
return
}
if eq.left.typ() != eq.right.typ() {
eq.left = &toStringExpr{expr: eq.left}
eq.right = &toStringExpr{expr: eq.right}
}
// General case
eq.left.emit(gctx)
if eq.isEq {

View File

@@ -1044,48 +1044,54 @@ func (ctx *parseContext) parseCompare(cond *mkparser.Directive) starlarkExpr {
args[1].TrimLeftSpaces()
isEq := !strings.HasSuffix(cond.Name, "neq")
switch xLeft := ctx.parseMakeString(cond, args[0]).(type) {
case *stringLiteralExpr, *variableRefExpr:
switch xRight := ctx.parseMakeString(cond, args[1]).(type) {
case *stringLiteralExpr, *variableRefExpr:
return &eqExpr{left: xLeft, right: xRight, isEq: isEq}
case *badExpr:
return xRight
default:
expr, ok := ctx.parseCheckFunctionCallResult(cond, xLeft, args[1])
if ok {
return expr
}
return ctx.newBadExpr(cond, "right operand is too complex: %s", args[1].Dump())
}
case *badExpr:
return xLeft
default:
switch xRight := ctx.parseMakeString(cond, args[1]).(type) {
case *stringLiteralExpr, *variableRefExpr:
expr, ok := ctx.parseCheckFunctionCallResult(cond, xRight, args[0])
if ok {
return expr
}
return ctx.newBadExpr(cond, "left operand is too complex: %s", args[0].Dump())
case *badExpr:
return xRight
default:
return ctx.newBadExpr(cond, "operands are too complex: (%s,%s)", args[0].Dump(), args[1].Dump())
}
xLeft := ctx.parseMakeString(cond, args[0])
xRight := ctx.parseMakeString(cond, args[1])
if bad, ok := xLeft.(*badExpr); ok {
return bad
}
if bad, ok := xRight.(*badExpr); ok {
return bad
}
if expr, ok := ctx.parseCompareSpecialCases(cond, xLeft, xRight); ok {
return expr
}
return &eqExpr{left: xLeft, right: xRight, isEq: isEq}
}
func (ctx *parseContext) parseCheckFunctionCallResult(directive *mkparser.Directive, xValue starlarkExpr,
varArg *mkparser.MakeString) (starlarkExpr, bool) {
mkSingleVar, ok := varArg.SingleVariable()
if !ok {
// Given an if statement's directive and the left/right starlarkExprs,
// check if the starlarkExprs are one of a few hardcoded special cases
// that can be converted to a simpler equalify expression than simply comparing
// the two.
func (ctx *parseContext) parseCompareSpecialCases(directive *mkparser.Directive, left starlarkExpr,
right starlarkExpr) (starlarkExpr, bool) {
isEq := !strings.HasSuffix(directive.Name, "neq")
// All the special cases require a call on one side and a
// string literal/variable on the other. Turn the left/right variables into
// call/value variables, and return false if that's not possible.
var value starlarkExpr = nil
call, ok := left.(*callExpr)
if ok {
switch right.(type) {
case *stringLiteralExpr, *variableRefExpr:
value = right
}
} else {
call, _ = right.(*callExpr)
switch left.(type) {
case *stringLiteralExpr, *variableRefExpr:
value = left
}
}
if call == nil || value == nil {
return nil, false
}
expr := ctx.parseReference(directive, mkSingleVar)
negate := strings.HasSuffix(directive.Name, "neq")
checkIsSomethingFunction := func(xCall *callExpr) starlarkExpr {
s, ok := maybeString(xValue)
s, ok := maybeString(value)
if !ok || s != "true" {
return ctx.newBadExpr(directive,
fmt.Sprintf("the result of %s can be compared only to 'true'", xCall.name))
@@ -1095,97 +1101,90 @@ func (ctx *parseContext) parseCheckFunctionCallResult(directive *mkparser.Direct
}
return nil
}
switch x := expr.(type) {
case *callExpr:
switch x.name {
case "filter":
return ctx.parseCompareFilterFuncResult(directive, x, xValue, !negate), true
case "filter-out":
return ctx.parseCompareFilterFuncResult(directive, x, xValue, negate), true
case "wildcard":
return ctx.parseCompareWildcardFuncResult(directive, x, xValue, negate), true
case "findstring":
return ctx.parseCheckFindstringFuncResult(directive, x, xValue, negate), true
case "strip":
return ctx.parseCompareStripFuncResult(directive, x, xValue, negate), true
case "is-board-platform":
if xBad := checkIsSomethingFunction(x); xBad != nil {
return xBad, true
}
return &eqExpr{
left: &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
right: x.args[0],
isEq: !negate,
}, true
case "is-board-platform-in-list":
if xBad := checkIsSomethingFunction(x); xBad != nil {
return xBad, true
}
return &inExpr{
expr: &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
list: maybeConvertToStringList(x.args[0]),
isNot: negate,
}, true
case "is-product-in-list":
if xBad := checkIsSomethingFunction(x); xBad != nil {
return xBad, true
}
return &inExpr{
expr: &variableRefExpr{ctx.addVariable("TARGET_PRODUCT"), true},
list: maybeConvertToStringList(x.args[0]),
isNot: negate,
}, true
case "is-vendor-board-platform":
if xBad := checkIsSomethingFunction(x); xBad != nil {
return xBad, true
}
s, ok := maybeString(x.args[0])
if !ok {
return ctx.newBadExpr(directive, "cannot handle non-constant argument to is-vendor-board-platform"), true
}
return &inExpr{
expr: &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
list: &variableRefExpr{ctx.addVariable(s + "_BOARD_PLATFORMS"), true},
isNot: negate,
}, true
case "is-board-platform2", "is-board-platform-in-list2":
if s, ok := maybeString(xValue); !ok || s != "" {
return ctx.newBadExpr(directive,
fmt.Sprintf("the result of %s can be compared only to empty", x.name)), true
}
if len(x.args) != 1 {
return ctx.newBadExpr(directive, "%s requires an argument", x.name), true
}
cc := &callExpr{
name: x.name,
args: []starlarkExpr{x.args[0]},
returnType: starlarkTypeBool,
}
if !negate {
return &notExpr{cc}, true
}
return cc, true
case "is-vendor-board-qcom":
if s, ok := maybeString(xValue); !ok || s != "" {
return ctx.newBadExpr(directive,
fmt.Sprintf("the result of %s can be compared only to empty", x.name)), true
}
// if the expression is ifneq (,$(call is-vendor-board-platform,...)), negate==true,
// so we should set inExpr.isNot to false
return &inExpr{
expr: &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
list: &variableRefExpr{ctx.addVariable("QCOM_BOARD_PLATFORMS"), true},
isNot: !negate,
}, true
default:
return ctx.newBadExpr(directive, "Unknown function in ifeq: %s", x.name), true
switch call.name {
case "filter":
return ctx.parseCompareFilterFuncResult(directive, call, value, isEq), true
case "filter-out":
return ctx.parseCompareFilterFuncResult(directive, call, value, !isEq), true
case "wildcard":
return ctx.parseCompareWildcardFuncResult(directive, call, value, !isEq), true
case "findstring":
return ctx.parseCheckFindstringFuncResult(directive, call, value, !isEq), true
case "strip":
return ctx.parseCompareStripFuncResult(directive, call, value, !isEq), true
case "is-board-platform":
if xBad := checkIsSomethingFunction(call); xBad != nil {
return xBad, true
}
case *badExpr:
return x, true
default:
return nil, false
return &eqExpr{
left: &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
right: call.args[0],
isEq: isEq,
}, true
case "is-board-platform-in-list":
if xBad := checkIsSomethingFunction(call); xBad != nil {
return xBad, true
}
return &inExpr{
expr: &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
list: maybeConvertToStringList(call.args[0]),
isNot: !isEq,
}, true
case "is-product-in-list":
if xBad := checkIsSomethingFunction(call); xBad != nil {
return xBad, true
}
return &inExpr{
expr: &variableRefExpr{ctx.addVariable("TARGET_PRODUCT"), true},
list: maybeConvertToStringList(call.args[0]),
isNot: !isEq,
}, true
case "is-vendor-board-platform":
if xBad := checkIsSomethingFunction(call); xBad != nil {
return xBad, true
}
s, ok := maybeString(call.args[0])
if !ok {
return ctx.newBadExpr(directive, "cannot handle non-constant argument to is-vendor-board-platform"), true
}
return &inExpr{
expr: &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
list: &variableRefExpr{ctx.addVariable(s + "_BOARD_PLATFORMS"), true},
isNot: !isEq,
}, true
case "is-board-platform2", "is-board-platform-in-list2":
if s, ok := maybeString(value); !ok || s != "" {
return ctx.newBadExpr(directive,
fmt.Sprintf("the result of %s can be compared only to empty", call.name)), true
}
if len(call.args) != 1 {
return ctx.newBadExpr(directive, "%s requires an argument", call.name), true
}
cc := &callExpr{
name: call.name,
args: []starlarkExpr{call.args[0]},
returnType: starlarkTypeBool,
}
if isEq {
return &notExpr{cc}, true
}
return cc, true
case "is-vendor-board-qcom":
if s, ok := maybeString(value); !ok || s != "" {
return ctx.newBadExpr(directive,
fmt.Sprintf("the result of %s can be compared only to empty", call.name)), true
}
// if the expression is ifneq (,$(call is-vendor-board-platform,...)), negate==true,
// so we should set inExpr.isNot to false
return &inExpr{
expr: &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
list: &variableRefExpr{ctx.addVariable("QCOM_BOARD_PLATFORMS"), true},
isNot: isEq,
}, true
}
return nil, false
}
func (ctx *parseContext) parseCompareFilterFuncResult(cond *mkparser.Directive,

View File

@@ -350,6 +350,21 @@ def init(g, handle):
cfg["PRODUCT_MODEL"] = "pix21"
if "aosp_x86" != g["TARGET_PRODUCT"]:
cfg["PRODUCT_MODEL"] = "pix3"
`,
},
{
desc: "ifeq with soong_config_get",
mkname: "product.mk",
in: `
ifeq (true,$(call soong_config_get,art_module,source_build))
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if "true" == rblf.soong_config_get(g, "art_module", "source_build"):
pass
`,
},
{
@@ -512,6 +527,21 @@ def init(g, handle):
pass
if rblf.file_wildcard_exists("foo*.mk"):
pass
`,
},
{
desc: "if with interpolation",
mkname: "product.mk",
in: `
ifeq ($(VARIABLE1)text$(VARIABLE2),true)
endif
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
def init(g, handle):
cfg = rblf.cfg(handle)
if "%stext%s" % (g.get("VARIABLE1", ""), g.get("VARIABLE2", "")) == "true":
pass
`,
},
{