Loading mk2rbc/expr.go +74 −0 Original line number Diff line number Diff line Loading @@ -85,6 +85,31 @@ func (s *intLiteralExpr) emitListVarCopy(gctx *generationContext) { 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] Loading Loading @@ -617,6 +642,55 @@ func (cx *callExpr) emitListVarCopy(gctx *generationContext) { 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 Loading mk2rbc/mk2rbc.go +60 −0 Original line number Diff line number Diff line Loading @@ -112,6 +112,7 @@ var knownFunctions = map[string]struct { "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 Loading Loading @@ -1331,6 +1332,34 @@ func (ctx *parseContext) parseReference(node mkparser.Node, ref *mkparser.MakeSt // TODO (asmundak): if we find many, maybe handle them. return ctx.newBadExpr(node, "SOONG_CONFIG_ variables cannot be referenced, use soong_config_get instead: %s", refDump) } // Handle substitution references: https://www.gnu.org/software/make/manual/html_node/Substitution-Refs.html if strings.Contains(refDump, ":") { parts := strings.SplitN(refDump, ":", 2) substParts := strings.SplitN(parts[1], "=", 2) if len(substParts) < 2 || strings.Count(substParts[0], "%") > 1 { return ctx.newBadExpr(node, "Invalid substitution reference") } if !strings.Contains(substParts[0], "%") { if strings.Contains(substParts[1], "%") { return ctx.newBadExpr(node, "A substitution reference must have a %% in the \"before\" part of the substitution if it has one in the \"after\" part.") } substParts[0] = "%" + substParts[0] substParts[1] = "%" + substParts[1] } v := ctx.addVariable(parts[0]) if v == nil { return ctx.newBadExpr(node, "unknown variable %s", refDump) } return &callExpr{ name: "patsubst", returnType: knownFunctions["patsubst"].returnType, args: []starlarkExpr{ &stringLiteralExpr{literal: substParts[0]}, &stringLiteralExpr{literal: substParts[1]}, &variableRefExpr{v, ctx.lastAssignment(v.name()) != nil}, }, } } if v := ctx.addVariable(refDump); v != nil { return &variableRefExpr{v, ctx.lastAssignment(v.name()) != nil} } Loading Loading @@ -1368,6 +1397,8 @@ func (ctx *parseContext) parseReference(node mkparser.Node, ref *mkparser.MakeSt 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": Loading Loading @@ -1423,6 +1454,35 @@ func (ctx *parseContext) parseSubstFunc(node mkparser.Node, fname string, args * } } 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 { Loading mk2rbc/mk2rbc_test.go +40 −0 Original line number Diff line number Diff line Loading @@ -1089,6 +1089,46 @@ def init(g, handle): cfg = rblf.cfg(handle) if rblf.mk2rbc_error("build/product.mk:2", "cannot handle invoking foobar"): 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 []) `, }, { desc: "substitution references", mkname: "product.mk", in: ` SOURCES := foo.c bar.c OBJECTS := $(SOURCES:.c=.o) OBJECTS2 := $(SOURCES:%.c=%.o) `, expected: `load("//build/make/core:product_config.rbc", "rblf") def init(g, handle): cfg = rblf.cfg(handle) g["SOURCES"] = "foo.c bar.c" g["OBJECTS"] = rblf.mkpatsubst("%.c", "%.o", g["SOURCES"]) g["OBJECTS2"] = rblf.mkpatsubst("%.c", "%.o", g["SOURCES"]) `, }, } Loading Loading
mk2rbc/expr.go +74 −0 Original line number Diff line number Diff line Loading @@ -85,6 +85,31 @@ func (s *intLiteralExpr) emitListVarCopy(gctx *generationContext) { 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] Loading Loading @@ -617,6 +642,55 @@ func (cx *callExpr) emitListVarCopy(gctx *generationContext) { 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 Loading
mk2rbc/mk2rbc.go +60 −0 Original line number Diff line number Diff line Loading @@ -112,6 +112,7 @@ var knownFunctions = map[string]struct { "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 Loading Loading @@ -1331,6 +1332,34 @@ func (ctx *parseContext) parseReference(node mkparser.Node, ref *mkparser.MakeSt // TODO (asmundak): if we find many, maybe handle them. return ctx.newBadExpr(node, "SOONG_CONFIG_ variables cannot be referenced, use soong_config_get instead: %s", refDump) } // Handle substitution references: https://www.gnu.org/software/make/manual/html_node/Substitution-Refs.html if strings.Contains(refDump, ":") { parts := strings.SplitN(refDump, ":", 2) substParts := strings.SplitN(parts[1], "=", 2) if len(substParts) < 2 || strings.Count(substParts[0], "%") > 1 { return ctx.newBadExpr(node, "Invalid substitution reference") } if !strings.Contains(substParts[0], "%") { if strings.Contains(substParts[1], "%") { return ctx.newBadExpr(node, "A substitution reference must have a %% in the \"before\" part of the substitution if it has one in the \"after\" part.") } substParts[0] = "%" + substParts[0] substParts[1] = "%" + substParts[1] } v := ctx.addVariable(parts[0]) if v == nil { return ctx.newBadExpr(node, "unknown variable %s", refDump) } return &callExpr{ name: "patsubst", returnType: knownFunctions["patsubst"].returnType, args: []starlarkExpr{ &stringLiteralExpr{literal: substParts[0]}, &stringLiteralExpr{literal: substParts[1]}, &variableRefExpr{v, ctx.lastAssignment(v.name()) != nil}, }, } } if v := ctx.addVariable(refDump); v != nil { return &variableRefExpr{v, ctx.lastAssignment(v.name()) != nil} } Loading Loading @@ -1368,6 +1397,8 @@ func (ctx *parseContext) parseReference(node mkparser.Node, ref *mkparser.MakeSt 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": Loading Loading @@ -1423,6 +1454,35 @@ func (ctx *parseContext) parseSubstFunc(node mkparser.Node, fname string, args * } } 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 { Loading
mk2rbc/mk2rbc_test.go +40 −0 Original line number Diff line number Diff line Loading @@ -1089,6 +1089,46 @@ def init(g, handle): cfg = rblf.cfg(handle) if rblf.mk2rbc_error("build/product.mk:2", "cannot handle invoking foobar"): 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 []) `, }, { desc: "substitution references", mkname: "product.mk", in: ` SOURCES := foo.c bar.c OBJECTS := $(SOURCES:.c=.o) OBJECTS2 := $(SOURCES:%.c=%.o) `, expected: `load("//build/make/core:product_config.rbc", "rblf") def init(g, handle): cfg = rblf.cfg(handle) g["SOURCES"] = "foo.c bar.c" g["OBJECTS"] = rblf.mkpatsubst("%.c", "%.o", g["SOURCES"]) g["OBJECTS2"] = rblf.mkpatsubst("%.c", "%.o", g["SOURCES"]) `, }, } Loading