Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c0bd6986 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge changes I5ba5e518,Icdf30c4d

* changes:
  Handle substitution references in mk2rbc
  Support if expressions in mk2rbc
parents e3d50bfb c36c9626
Loading
Loading
Loading
Loading
+74 −0
Original line number Diff line number Diff line
@@ -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]
@@ -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
+60 −0
Original line number Diff line number Diff line
@@ -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
@@ -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}
		}
@@ -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":
@@ -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 {
+40 −0
Original line number Diff line number Diff line
@@ -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"])
`,
	},
}