diff --git a/mk2rbc/expr.go b/mk2rbc/expr.go index e78f49273..3f355ac84 100644 --- a/mk2rbc/expr.go +++ b/mk2rbc/expr.go @@ -728,6 +728,36 @@ func (f *foreachExpr) transform(transformer func(expr starlarkExpr) starlarkExpr } } +type binaryOpExpr struct { + left, right starlarkExpr + op string + returnType starlarkType +} + +func (b *binaryOpExpr) emit(gctx *generationContext) { + b.left.emit(gctx) + gctx.write(" " + b.op + " ") + b.right.emit(gctx) +} + +func (b *binaryOpExpr) typ() starlarkType { + return b.returnType +} + +func (b *binaryOpExpr) emitListVarCopy(gctx *generationContext) { + b.emit(gctx) +} + +func (b *binaryOpExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr { + b.left = b.left.transform(transformer) + b.right = b.right.transform(transformer) + if replacement := transformer(b); replacement != nil { + return replacement + } else { + return b + } +} + type badExpr struct { errorLocation ErrorLocation message string diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go index 04038e46c..28c4f7b32 100644 --- a/mk2rbc/mk2rbc.go +++ b/mk2rbc/mk2rbc.go @@ -100,6 +100,11 @@ var knownFunctions = map[string]interface { "is-vendor-board-qcom": &isVendorBoardQcomCallParser{}, "lastword": &firstOrLastwordCallParser{isLastWord: true}, "notdir": &simpleCallParser{name: baseName + ".notdir", returnType: starlarkTypeString, addGlobals: false}, + "math_max": &mathMaxOrMinCallParser{function: "max"}, + "math_min": &mathMaxOrMinCallParser{function: "min"}, + "math_gt_or_eq": &mathComparisonCallParser{op: ">="}, + "math_gt": &mathComparisonCallParser{op: ">"}, + "math_lt": &mathComparisonCallParser{op: "<"}, "my-dir": &myDirCallParser{}, "patsubst": &substCallParser{fname: "patsubst"}, "product-copy-files-by-pattern": &simpleCallParser{name: baseName + ".product_copy_files_by_pattern", returnType: starlarkTypeList, addGlobals: false}, @@ -1064,6 +1069,23 @@ func (ctx *parseContext) parseCompare(cond *mkparser.Directive) starlarkExpr { case *eqExpr: typedExpr.isEq = !typedExpr.isEq return typedExpr + case *binaryOpExpr: + switch typedExpr.op { + case ">": + typedExpr.op = "<=" + return typedExpr + case "<": + typedExpr.op = ">=" + return typedExpr + case ">=": + typedExpr.op = "<" + return typedExpr + case "<=": + typedExpr.op = ">" + return typedExpr + default: + return ¬Expr{expr: expr} + } default: return ¬Expr{expr: expr} } @@ -1086,6 +1108,13 @@ func (ctx *parseContext) parseCompare(cond *mkparser.Directive) starlarkExpr { return otherOperand } } + if intOperand, err := strconv.Atoi(strings.TrimSpace(stringOperand)); err == nil && otherOperand.typ() == starlarkTypeInt { + return &eqExpr{ + left: otherOperand, + right: &intLiteralExpr{literal: intOperand}, + isEq: isEq, + } + } } return &eqExpr{left: xLeft, right: xRight, isEq: isEq} @@ -1621,6 +1650,68 @@ func (p *firstOrLastwordCallParser) parse(ctx *parseContext, node mkparser.Node, return &indexExpr{&callExpr{object: arg, name: "split", returnType: starlarkTypeList}, index} } +func parseIntegerArguments(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString, expectedArgs int) ([]starlarkExpr, error) { + parsedArgs := make([]starlarkExpr, 0) + for _, arg := range args.Split(",") { + expr := ctx.parseMakeString(node, arg) + if expr.typ() == starlarkTypeList { + return nil, fmt.Errorf("argument to math argument has type list, which cannot be converted to int") + } + if s, ok := maybeString(expr); ok { + intVal, err := strconv.Atoi(strings.TrimSpace(s)) + if err != nil { + return nil, err + } + expr = &intLiteralExpr{literal: intVal} + } else if expr.typ() != starlarkTypeInt { + expr = &callExpr{ + name: "int", + args: []starlarkExpr{expr}, + returnType: starlarkTypeInt, + } + } + parsedArgs = append(parsedArgs, expr) + } + if len(parsedArgs) != expectedArgs { + return nil, fmt.Errorf("function should have %d arguments", expectedArgs) + } + return parsedArgs, nil +} + +type mathComparisonCallParser struct { + op string +} + +func (p *mathComparisonCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { + parsedArgs, err := parseIntegerArguments(ctx, node, args, 2) + if err != nil { + return ctx.newBadExpr(node, err.Error()) + } + return &binaryOpExpr{ + left: parsedArgs[0], + right: parsedArgs[1], + op: p.op, + returnType: starlarkTypeBool, + } +} + +type mathMaxOrMinCallParser struct { + function string +} + +func (p *mathMaxOrMinCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { + parsedArgs, err := parseIntegerArguments(ctx, node, args, 2) + if err != nil { + return ctx.newBadExpr(node, err.Error()) + } + return &callExpr{ + object: nil, + name: p.function, + args: parsedArgs, + returnType: starlarkTypeInt, + } +} + func (ctx *parseContext) parseMakeString(node mkparser.Node, mk *mkparser.MakeString) starlarkExpr { if mk.Const() { return &stringLiteralExpr{mk.Dump()} diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go index 1ba273b41..7f4de8067 100644 --- a/mk2rbc/mk2rbc_test.go +++ b/mk2rbc/mk2rbc_test.go @@ -1239,6 +1239,63 @@ def init(g, handle): g["NATIVE_BRIDGE_PRODUCT_PACKAGES"] = "libnative_bridge_vdso.native_bridge native_bridge_guest_app_process.native_bridge native_bridge_guest_linker.native_bridge" g["NATIVE_BRIDGE_MODIFIED_GUEST_LIBS"] = "libaaudio libamidi libandroid libandroid_runtime" g["NATIVE_BRIDGE_PRODUCT_PACKAGES"] += " " + " ".join(rblf.addsuffix(".native_bridge", g.get("NATIVE_BRIDGE_ORIG_GUEST_LIBS", ""))) +`, + }, + { + desc: "Math functions", + mkname: "product.mk", + in: ` +# Test the math functions defined in build/make/common/math.mk +ifeq ($(call math_max,2,5),5) +endif +ifeq ($(call math_min,2,5),2) +endif +ifeq ($(call math_gt_or_eq,2,5),true) +endif +ifeq ($(call math_gt,2,5),true) +endif +ifeq ($(call math_lt,2,5),true) +endif +ifeq ($(call math_gt_or_eq,2,5),) +endif +ifeq ($(call math_gt,2,5),) +endif +ifeq ($(call math_lt,2,5),) +endif +ifeq ($(call math_gt_or_eq,$(MY_VAR), 5),true) +endif +ifeq ($(call math_gt_or_eq,$(MY_VAR),$(MY_OTHER_VAR)),true) +endif +ifeq ($(call math_gt_or_eq,100$(MY_VAR),10),true) +endif +`, + expected: `# Test the math functions defined in build/make/common/math.mk +load("//build/make/core:product_config.rbc", "rblf") + +def init(g, handle): + cfg = rblf.cfg(handle) + if max(2, 5) == 5: + pass + if min(2, 5) == 2: + pass + if 2 >= 5: + pass + if 2 > 5: + pass + if 2 < 5: + pass + if 2 < 5: + pass + if 2 <= 5: + pass + if 2 >= 5: + pass + if int(g.get("MY_VAR", "")) >= 5: + pass + if int(g.get("MY_VAR", "")) >= int(g.get("MY_OTHER_VAR", "")): + pass + if int("100%s" % g.get("MY_VAR", "")) >= 10: + pass `, }, }