Merge "Return starlarkNodes from the functions that parse them" am: 5a95bcac4a am: 06f61753ac

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

Change-Id: I2444ba3d6d18130138f94791809ed35e68e70d24
This commit is contained in:
Cole Faust
2022-02-03 19:40:05 +00:00
committed by Automerger Merge Worker
3 changed files with 104 additions and 189 deletions

View File

@@ -370,10 +370,6 @@ func init() {
} }
} }
type nodeReceiver interface {
newNode(node starlarkNode)
}
// Information about the generated Starlark script. // Information about the generated Starlark script.
type StarlarkScript struct { type StarlarkScript struct {
mkFile string mkFile string
@@ -389,10 +385,6 @@ type StarlarkScript struct {
nodeLocator func(pos mkparser.Pos) int nodeLocator func(pos mkparser.Pos) int
} }
func (ss *StarlarkScript) newNode(node starlarkNode) {
ss.nodes = append(ss.nodes, node)
}
// varAssignmentScope points to the last assignment for each variable // varAssignmentScope points to the last assignment for each variable
// in the current block. It is used during the parsing to chain // in the current block. It is used during the parsing to chain
// the assignments to a variable together. // the assignments to a variable together.
@@ -415,8 +407,6 @@ type parseContext struct {
tracedVariables map[string]bool // variables to be traced in the generated script tracedVariables map[string]bool // variables to be traced in the generated script
variables map[string]variable variables map[string]variable
varAssignments *varAssignmentScope varAssignments *varAssignmentScope
receiver nodeReceiver // receptacle for the generated starlarkNode's
receiverStack []nodeReceiver
outputDir string outputDir string
dependentModules map[string]*moduleInfo dependentModules map[string]*moduleInfo
soongNamespaces map[string]map[string]bool soongNamespaces map[string]map[string]bool
@@ -503,20 +493,6 @@ func (ctx *parseContext) popVarAssignments() {
ctx.varAssignments = ctx.varAssignments.outer ctx.varAssignments = ctx.varAssignments.outer
} }
func (ctx *parseContext) pushReceiver(rcv nodeReceiver) {
ctx.receiverStack = append(ctx.receiverStack, ctx.receiver)
ctx.receiver = rcv
}
func (ctx *parseContext) popReceiver() {
last := len(ctx.receiverStack) - 1
if last < 0 {
panic(fmt.Errorf("popReceiver: receiver stack empty"))
}
ctx.receiver = ctx.receiverStack[last]
ctx.receiverStack = ctx.receiverStack[0:last]
}
func (ctx *parseContext) hasNodes() bool { func (ctx *parseContext) hasNodes() bool {
return ctx.currentNodeIndex < len(ctx.nodes) return ctx.currentNodeIndex < len(ctx.nodes)
} }
@@ -537,11 +513,10 @@ func (ctx *parseContext) backNode() {
ctx.currentNodeIndex-- ctx.currentNodeIndex--
} }
func (ctx *parseContext) handleAssignment(a *mkparser.Assignment) { func (ctx *parseContext) handleAssignment(a *mkparser.Assignment) []starlarkNode {
// Handle only simple variables // Handle only simple variables
if !a.Name.Const() { if !a.Name.Const() {
ctx.errorf(a, "Only simple variables are handled") return []starlarkNode{ctx.newBadNode(a, "Only simple variables are handled")}
return
} }
name := a.Name.Strings[0] name := a.Name.Strings[0]
// The `override` directive // The `override` directive
@@ -549,18 +524,16 @@ func (ctx *parseContext) handleAssignment(a *mkparser.Assignment) {
// is parsed as an assignment to a variable named `override FOO`. // is parsed as an assignment to a variable named `override FOO`.
// There are very few places where `override` is used, just flag it. // There are very few places where `override` is used, just flag it.
if strings.HasPrefix(name, "override ") { if strings.HasPrefix(name, "override ") {
ctx.errorf(a, "cannot handle override directive") return []starlarkNode{ctx.newBadNode(a, "cannot handle override directive")}
} }
// Soong configuration // Soong configuration
if strings.HasPrefix(name, soongNsPrefix) { if strings.HasPrefix(name, soongNsPrefix) {
ctx.handleSoongNsAssignment(strings.TrimPrefix(name, soongNsPrefix), a) return ctx.handleSoongNsAssignment(strings.TrimPrefix(name, soongNsPrefix), a)
return
} }
lhs := ctx.addVariable(name) lhs := ctx.addVariable(name)
if lhs == nil { if lhs == nil {
ctx.errorf(a, "unknown variable %s", name) return []starlarkNode{ctx.newBadNode(a, "unknown variable %s", name)}
return
} }
_, isTraced := ctx.tracedVariables[name] _, isTraced := ctx.tracedVariables[name]
asgn := &assignmentNode{lhs: lhs, mkValue: a.Value, isTraced: isTraced, location: ctx.errorLocation(a)} asgn := &assignmentNode{lhs: lhs, mkValue: a.Value, isTraced: isTraced, location: ctx.errorLocation(a)}
@@ -568,8 +541,7 @@ func (ctx *parseContext) handleAssignment(a *mkparser.Assignment) {
// Try to divine variable type from the RHS // Try to divine variable type from the RHS
asgn.value = ctx.parseMakeString(a, a.Value) asgn.value = ctx.parseMakeString(a, a.Value)
if xBad, ok := asgn.value.(*badExpr); ok { if xBad, ok := asgn.value.(*badExpr); ok {
ctx.wrapBadExpr(xBad) return []starlarkNode{&exprNode{xBad}}
return
} }
inferred_type := asgn.value.typ() inferred_type := asgn.value.typ()
if inferred_type != starlarkTypeUnknown { if inferred_type != starlarkTypeUnknown {
@@ -577,9 +549,9 @@ func (ctx *parseContext) handleAssignment(a *mkparser.Assignment) {
} }
} }
if lhs.valueType() == starlarkTypeList { if lhs.valueType() == starlarkTypeList {
xConcat := ctx.buildConcatExpr(a) xConcat, xBad := ctx.buildConcatExpr(a)
if xConcat == nil { if xBad != nil {
return return []starlarkNode{&exprNode{expr: xBad}}
} }
switch len(xConcat.items) { switch len(xConcat.items) {
case 0: case 0:
@@ -592,8 +564,7 @@ func (ctx *parseContext) handleAssignment(a *mkparser.Assignment) {
} else { } else {
asgn.value = ctx.parseMakeString(a, a.Value) asgn.value = ctx.parseMakeString(a, a.Value)
if xBad, ok := asgn.value.(*badExpr); ok { if xBad, ok := asgn.value.(*badExpr); ok {
ctx.wrapBadExpr(xBad) return []starlarkNode{&exprNode{expr: xBad}}
return
} }
} }
@@ -614,14 +585,13 @@ func (ctx *parseContext) handleAssignment(a *mkparser.Assignment) {
panic(fmt.Errorf("unexpected assignment type %s", a.Type)) panic(fmt.Errorf("unexpected assignment type %s", a.Type))
} }
ctx.receiver.newNode(asgn) return []starlarkNode{asgn}
} }
func (ctx *parseContext) handleSoongNsAssignment(name string, asgn *mkparser.Assignment) { func (ctx *parseContext) handleSoongNsAssignment(name string, asgn *mkparser.Assignment) []starlarkNode {
val := ctx.parseMakeString(asgn, asgn.Value) val := ctx.parseMakeString(asgn, asgn.Value)
if xBad, ok := val.(*badExpr); ok { if xBad, ok := val.(*badExpr); ok {
ctx.wrapBadExpr(xBad) return []starlarkNode{&exprNode{expr: xBad}}
return
} }
// Unfortunately, Soong namespaces can be set up by directly setting corresponding Make // Unfortunately, Soong namespaces can be set up by directly setting corresponding Make
@@ -634,17 +604,18 @@ func (ctx *parseContext) handleSoongNsAssignment(name string, asgn *mkparser.Ass
// $(call add_soong_config_namespace,foo) // $(call add_soong_config_namespace,foo)
s, ok := maybeString(val) s, ok := maybeString(val)
if !ok { if !ok {
ctx.errorf(asgn, "cannot handle variables in SOONG_CONFIG_NAMESPACES assignment, please use add_soong_config_namespace instead") return []starlarkNode{ctx.newBadNode(asgn, "cannot handle variables in SOONG_CONFIG_NAMESPACES assignment, please use add_soong_config_namespace instead")}
return
} }
result := make([]starlarkNode, 0)
for _, ns := range strings.Fields(s) { for _, ns := range strings.Fields(s) {
ctx.addSoongNamespace(ns) ctx.addSoongNamespace(ns)
ctx.receiver.newNode(&exprNode{&callExpr{ result = append(result, &exprNode{&callExpr{
name: baseName + ".soong_config_namespace", name: baseName + ".soong_config_namespace",
args: []starlarkExpr{&globalsExpr{}, &stringLiteralExpr{ns}}, args: []starlarkExpr{&globalsExpr{}, &stringLiteralExpr{ns}},
returnType: starlarkTypeVoid, returnType: starlarkTypeVoid,
}}) }})
} }
return result
} else { } else {
// Upon seeing // Upon seeing
// SOONG_CONFIG_x_y = v // SOONG_CONFIG_x_y = v
@@ -664,45 +635,41 @@ func (ctx *parseContext) handleSoongNsAssignment(name string, asgn *mkparser.Ass
continue continue
} }
if namespaceName != "" { if namespaceName != "" {
ctx.errorf(asgn, "ambiguous soong namespace (may be either `%s` or `%s`)", namespaceName, name[0:pos]) return []starlarkNode{ctx.newBadNode(asgn, "ambiguous soong namespace (may be either `%s` or `%s`)", namespaceName, name[0:pos])}
return
} }
namespaceName = name[0:pos] namespaceName = name[0:pos]
varName = name[pos+1:] varName = name[pos+1:]
} }
if namespaceName == "" { if namespaceName == "" {
ctx.errorf(asgn, "cannot figure out Soong namespace, please use add_soong_config_var_value macro instead") return []starlarkNode{ctx.newBadNode(asgn, "cannot figure out Soong namespace, please use add_soong_config_var_value macro instead")}
return
} }
if varName == "" { if varName == "" {
// Remember variables in this namespace // Remember variables in this namespace
s, ok := maybeString(val) s, ok := maybeString(val)
if !ok { if !ok {
ctx.errorf(asgn, "cannot handle variables in SOONG_CONFIG_ assignment, please use add_soong_config_var_value instead") return []starlarkNode{ctx.newBadNode(asgn, "cannot handle variables in SOONG_CONFIG_ assignment, please use add_soong_config_var_value instead")}
return
} }
ctx.updateSoongNamespace(asgn.Type != "+=", namespaceName, strings.Fields(s)) ctx.updateSoongNamespace(asgn.Type != "+=", namespaceName, strings.Fields(s))
return return []starlarkNode{}
} }
// Finally, handle assignment to a namespace variable // Finally, handle assignment to a namespace variable
if !ctx.hasNamespaceVar(namespaceName, varName) { if !ctx.hasNamespaceVar(namespaceName, varName) {
ctx.errorf(asgn, "no %s variable in %s namespace, please use add_soong_config_var_value instead", varName, namespaceName) return []starlarkNode{ctx.newBadNode(asgn, "no %s variable in %s namespace, please use add_soong_config_var_value instead", varName, namespaceName)}
return
} }
fname := baseName + "." + soongConfigAssign fname := baseName + "." + soongConfigAssign
if asgn.Type == "+=" { if asgn.Type == "+=" {
fname = baseName + "." + soongConfigAppend fname = baseName + "." + soongConfigAppend
} }
ctx.receiver.newNode(&exprNode{&callExpr{ return []starlarkNode{&exprNode{&callExpr{
name: fname, name: fname,
args: []starlarkExpr{&globalsExpr{}, &stringLiteralExpr{namespaceName}, &stringLiteralExpr{varName}, val}, args: []starlarkExpr{&globalsExpr{}, &stringLiteralExpr{namespaceName}, &stringLiteralExpr{varName}, val},
returnType: starlarkTypeVoid, returnType: starlarkTypeVoid,
}}) }}}
} }
} }
func (ctx *parseContext) buildConcatExpr(a *mkparser.Assignment) *concatExpr { func (ctx *parseContext) buildConcatExpr(a *mkparser.Assignment) (*concatExpr, *badExpr) {
xConcat := &concatExpr{} xConcat := &concatExpr{}
var xItemList *listExpr var xItemList *listExpr
addToItemList := func(x ...starlarkExpr) { addToItemList := func(x ...starlarkExpr) {
@@ -724,8 +691,7 @@ func (ctx *parseContext) buildConcatExpr(a *mkparser.Assignment) *concatExpr {
// expressions return individual elements. // expressions return individual elements.
switch x := ctx.parseMakeString(a, item).(type) { switch x := ctx.parseMakeString(a, item).(type) {
case *badExpr: case *badExpr:
ctx.wrapBadExpr(x) return nil, x
return nil
case *stringLiteralExpr: case *stringLiteralExpr:
addToItemList(maybeConvertToStringList(x).(*listExpr).items...) addToItemList(maybeConvertToStringList(x).(*listExpr).items...)
default: default:
@@ -749,7 +715,7 @@ func (ctx *parseContext) buildConcatExpr(a *mkparser.Assignment) *concatExpr {
if xItemList != nil { if xItemList != nil {
xConcat.items = append(xConcat.items, xItemList) xConcat.items = append(xConcat.items, xItemList)
} }
return xConcat return xConcat, nil
} }
func (ctx *parseContext) newDependentModule(path string, optional bool) *moduleInfo { func (ctx *parseContext) newDependentModule(path string, optional bool) *moduleInfo {
@@ -779,7 +745,7 @@ func (ctx *parseContext) newDependentModule(path string, optional bool) *moduleI
} }
func (ctx *parseContext) handleSubConfig( func (ctx *parseContext) handleSubConfig(
v mkparser.Node, pathExpr starlarkExpr, loadAlways bool, processModule func(inheritedModule)) { v mkparser.Node, pathExpr starlarkExpr, loadAlways bool, processModule func(inheritedModule) starlarkNode) []starlarkNode {
// In a simple case, the name of a module to inherit/include is known statically. // In a simple case, the name of a module to inherit/include is known statically.
if path, ok := maybeString(pathExpr); ok { if path, ok := maybeString(pathExpr); ok {
@@ -788,18 +754,19 @@ func (ctx *parseContext) handleSubConfig(
moduleShouldExist := loadAlways && ctx.ifNestLevel == 0 moduleShouldExist := loadAlways && ctx.ifNestLevel == 0
if strings.Contains(path, "*") { if strings.Contains(path, "*") {
if paths, err := fs.Glob(ctx.script.sourceFS, path); err == nil { if paths, err := fs.Glob(ctx.script.sourceFS, path); err == nil {
result := make([]starlarkNode, 0)
for _, p := range paths { for _, p := range paths {
mi := ctx.newDependentModule(p, !moduleShouldExist) mi := ctx.newDependentModule(p, !moduleShouldExist)
processModule(inheritedStaticModule{mi, loadAlways}) result = append(result, processModule(inheritedStaticModule{mi, loadAlways}))
} }
return result
} else { } else {
ctx.errorf(v, "cannot glob wildcard argument") return []starlarkNode{ctx.newBadNode(v, "cannot glob wildcard argument")}
} }
} else { } else {
mi := ctx.newDependentModule(path, !moduleShouldExist) mi := ctx.newDependentModule(path, !moduleShouldExist)
processModule(inheritedStaticModule{mi, loadAlways}) return []starlarkNode{processModule(inheritedStaticModule{mi, loadAlways})}
} }
return
} }
// If module path references variables (e.g., $(v1)/foo/$(v2)/device-config.mk), find all the paths in the // If module path references variables (e.g., $(v1)/foo/$(v2)/device-config.mk), find all the paths in the
@@ -819,8 +786,7 @@ func (ctx *parseContext) handleSubConfig(
var matchingPaths []string var matchingPaths []string
varPath, ok := pathExpr.(*interpolateExpr) varPath, ok := pathExpr.(*interpolateExpr)
if !ok { if !ok {
ctx.errorf(v, "inherit-product/include argument is too complex") return []starlarkNode{ctx.newBadNode(v, "inherit-product/include argument is too complex")}
return
} }
pathPattern := []string{varPath.chunks[0]} pathPattern := []string{varPath.chunks[0]}
@@ -842,12 +808,11 @@ func (ctx *parseContext) handleSubConfig(
// Safeguard against $(call inherit-product,$(PRODUCT_PATH)) // Safeguard against $(call inherit-product,$(PRODUCT_PATH))
const maxMatchingFiles = 150 const maxMatchingFiles = 150
if len(matchingPaths) > maxMatchingFiles { if len(matchingPaths) > maxMatchingFiles {
ctx.errorf(v, "there are >%d files matching the pattern, please rewrite it", maxMatchingFiles) return []starlarkNode{ctx.newBadNode(v, "there are >%d files matching the pattern, please rewrite it", maxMatchingFiles)}
return
} }
if len(matchingPaths) == 1 { if len(matchingPaths) == 1 {
res := inheritedStaticModule{ctx.newDependentModule(matchingPaths[0], loadAlways && ctx.ifNestLevel == 0), loadAlways} res := inheritedStaticModule{ctx.newDependentModule(matchingPaths[0], loadAlways && ctx.ifNestLevel == 0), loadAlways}
processModule(res) return []starlarkNode{processModule(res)}
} else { } else {
needsWarning := pathPattern[0] == "" && len(ctx.includeTops) == 0 needsWarning := pathPattern[0] == "" && len(ctx.includeTops) == 0
res := inheritedDynamicModule{*varPath, []*moduleInfo{}, loadAlways, ctx.errorLocation(v), needsWarning} res := inheritedDynamicModule{*varPath, []*moduleInfo{}, loadAlways, ctx.errorLocation(v), needsWarning}
@@ -857,7 +822,7 @@ func (ctx *parseContext) handleSubConfig(
// by always loading the dynamic files as optional. // by always loading the dynamic files as optional.
res.candidateModules = append(res.candidateModules, ctx.newDependentModule(p, true)) res.candidateModules = append(res.candidateModules, ctx.newDependentModule(p, true))
} }
processModule(res) return []starlarkNode{processModule(res)}
} }
} }
@@ -885,25 +850,25 @@ func (ctx *parseContext) findMatchingPaths(pattern []string) []string {
return res return res
} }
func (ctx *parseContext) handleInheritModule(v mkparser.Node, args *mkparser.MakeString, loadAlways bool) { func (ctx *parseContext) handleInheritModule(v mkparser.Node, args *mkparser.MakeString, loadAlways bool) []starlarkNode {
args.TrimLeftSpaces() args.TrimLeftSpaces()
args.TrimRightSpaces() args.TrimRightSpaces()
pathExpr := ctx.parseMakeString(v, args) pathExpr := ctx.parseMakeString(v, args)
if _, ok := pathExpr.(*badExpr); ok { if _, ok := pathExpr.(*badExpr); ok {
ctx.errorf(v, "Unable to parse argument to inherit") return []starlarkNode{ctx.newBadNode(v, "Unable to parse argument to inherit")}
} }
ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) { return ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) starlarkNode {
ctx.receiver.newNode(&inheritNode{im, loadAlways}) return &inheritNode{im, loadAlways}
}) })
} }
func (ctx *parseContext) handleInclude(v mkparser.Node, pathExpr starlarkExpr, loadAlways bool) { func (ctx *parseContext) handleInclude(v mkparser.Node, pathExpr starlarkExpr, loadAlways bool) []starlarkNode {
ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) { return ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) starlarkNode {
ctx.receiver.newNode(&includeNode{im, loadAlways}) return &includeNode{im, loadAlways}
}) })
} }
func (ctx *parseContext) handleVariable(v *mkparser.Variable) { func (ctx *parseContext) handleVariable(v *mkparser.Variable) []starlarkNode {
// Handle: // Handle:
// $(call inherit-product,...) // $(call inherit-product,...)
// $(call inherit-product-if-exists,...) // $(call inherit-product-if-exists,...)
@@ -918,67 +883,57 @@ func (ctx *parseContext) handleVariable(v *mkparser.Variable) {
if strings.HasPrefix(v.Name.Dump(), "call inherit-product,") { if strings.HasPrefix(v.Name.Dump(), "call inherit-product,") {
args := v.Name.Clone() args := v.Name.Clone()
args.ReplaceLiteral("call inherit-product,", "") args.ReplaceLiteral("call inherit-product,", "")
ctx.handleInheritModule(v, args, true) return ctx.handleInheritModule(v, args, true)
return
} }
if strings.HasPrefix(v.Name.Dump(), "call inherit-product-if-exists,") { if strings.HasPrefix(v.Name.Dump(), "call inherit-product-if-exists,") {
args := v.Name.Clone() args := v.Name.Clone()
args.ReplaceLiteral("call inherit-product-if-exists,", "") args.ReplaceLiteral("call inherit-product-if-exists,", "")
ctx.handleInheritModule(v, args, false) return ctx.handleInheritModule(v, args, false)
return
}
expr := ctx.parseReference(v, v.Name)
switch x := expr.(type) {
case *callExpr:
ctx.receiver.newNode(&exprNode{expr})
case *badExpr:
ctx.wrapBadExpr(x)
default:
ctx.errorf(v, "cannot handle %s", v.Dump())
} }
return []starlarkNode{&exprNode{expr: ctx.parseReference(v, v.Name)}}
} }
func (ctx *parseContext) handleDefine(directive *mkparser.Directive) { func (ctx *parseContext) maybeHandleDefine(directive *mkparser.Directive) starlarkNode {
macro_name := strings.Fields(directive.Args.Strings[0])[0] macro_name := strings.Fields(directive.Args.Strings[0])[0]
// Ignore the macros that we handle // Ignore the macros that we handle
_, ignored := ignoredDefines[macro_name] _, ignored := ignoredDefines[macro_name]
_, known := knownFunctions[macro_name] _, known := knownFunctions[macro_name]
if !ignored && !known { if !ignored && !known {
ctx.errorf(directive, "define is not supported: %s", macro_name) return ctx.newBadNode(directive, "define is not supported: %s", macro_name)
} }
return nil
} }
func (ctx *parseContext) handleIfBlock(ifDirective *mkparser.Directive) { func (ctx *parseContext) handleIfBlock(ifDirective *mkparser.Directive) starlarkNode {
ssSwitch := &switchNode{} ssSwitch := &switchNode{
ctx.pushReceiver(ssSwitch) ssCases: []*switchCase{ctx.processBranch(ifDirective)},
for ctx.processBranch(ifDirective); ctx.hasNodes() && ctx.fatalError == nil; { }
for ctx.hasNodes() && ctx.fatalError == nil {
node := ctx.getNode() node := ctx.getNode()
switch x := node.(type) { switch x := node.(type) {
case *mkparser.Directive: case *mkparser.Directive:
switch x.Name { switch x.Name {
case "else", "elifdef", "elifndef", "elifeq", "elifneq": case "else", "elifdef", "elifndef", "elifeq", "elifneq":
ctx.processBranch(x) ssSwitch.ssCases = append(ssSwitch.ssCases, ctx.processBranch(x))
case "endif": case "endif":
ctx.popReceiver() return ssSwitch
ctx.receiver.newNode(ssSwitch)
return
default: default:
ctx.errorf(node, "unexpected directive %s", x.Name) return ctx.newBadNode(node, "unexpected directive %s", x.Name)
} }
default: default:
ctx.errorf(ifDirective, "unexpected statement") return ctx.newBadNode(ifDirective, "unexpected statement")
} }
} }
if ctx.fatalError == nil { if ctx.fatalError == nil {
ctx.fatalError = fmt.Errorf("no matching endif for %s", ifDirective.Dump()) ctx.fatalError = fmt.Errorf("no matching endif for %s", ifDirective.Dump())
} }
ctx.popReceiver() return ctx.newBadNode(ifDirective, "no matching endif for %s", ifDirective.Dump())
} }
// processBranch processes a single branch (if/elseif/else) until the next directive // processBranch processes a single branch (if/elseif/else) until the next directive
// on the same level. // on the same level.
func (ctx *parseContext) processBranch(check *mkparser.Directive) { func (ctx *parseContext) processBranch(check *mkparser.Directive) *switchCase {
block := switchCase{gate: ctx.parseCondition(check)} block := &switchCase{gate: ctx.parseCondition(check)}
defer func() { defer func() {
ctx.popVarAssignments() ctx.popVarAssignments()
ctx.ifNestLevel-- ctx.ifNestLevel--
@@ -987,29 +942,26 @@ func (ctx *parseContext) processBranch(check *mkparser.Directive) {
ctx.pushVarAssignments() ctx.pushVarAssignments()
ctx.ifNestLevel++ ctx.ifNestLevel++
ctx.pushReceiver(&block)
for ctx.hasNodes() { for ctx.hasNodes() {
node := ctx.getNode() node := ctx.getNode()
if d, ok := node.(*mkparser.Directive); ok { if d, ok := node.(*mkparser.Directive); ok {
switch d.Name { switch d.Name {
case "else", "elifdef", "elifndef", "elifeq", "elifneq", "endif": case "else", "elifdef", "elifndef", "elifeq", "elifneq", "endif":
ctx.popReceiver()
ctx.receiver.newNode(&block)
ctx.backNode() ctx.backNode()
return return block
} }
} }
ctx.handleSimpleStatement(node) block.nodes = append(block.nodes, ctx.handleSimpleStatement(node)...)
} }
ctx.fatalError = fmt.Errorf("no matching endif for %s", check.Dump()) ctx.fatalError = fmt.Errorf("no matching endif for %s", check.Dump())
ctx.popReceiver() return block
} }
func (ctx *parseContext) parseCondition(check *mkparser.Directive) starlarkNode { func (ctx *parseContext) parseCondition(check *mkparser.Directive) starlarkNode {
switch check.Name { switch check.Name {
case "ifdef", "ifndef", "elifdef", "elifndef": case "ifdef", "ifndef", "elifdef", "elifndef":
if !check.Args.Const() { if !check.Args.Const() {
return &exprNode{expr: ctx.newBadExpr(check, "ifdef variable ref too complex: %s", check.Args.Dump())} return ctx.newBadNode(check, "ifdef variable ref too complex: %s", check.Args.Dump())
} }
v := NewVariableRefExpr(ctx.addVariable(check.Args.Strings[0]), false) v := NewVariableRefExpr(ctx.addVariable(check.Args.Strings[0]), false)
if strings.HasSuffix(check.Name, "ndef") { if strings.HasSuffix(check.Name, "ndef") {
@@ -1032,12 +984,16 @@ func (ctx *parseContext) parseCondition(check *mkparser.Directive) starlarkNode
} }
func (ctx *parseContext) newBadExpr(node mkparser.Node, text string, args ...interface{}) starlarkExpr { func (ctx *parseContext) newBadExpr(node mkparser.Node, text string, args ...interface{}) starlarkExpr {
message := fmt.Sprintf(text, args...)
if ctx.errorLogger != nil { if ctx.errorLogger != nil {
ctx.errorLogger.NewError(ctx.errorLocation(node), node, text, args...) ctx.errorLogger.NewError(ctx.errorLocation(node), node, text, args...)
} }
ctx.script.hasErrors = true ctx.script.hasErrors = true
return &badExpr{errorLocation: ctx.errorLocation(node), message: message} return &badExpr{errorLocation: ctx.errorLocation(node), message: fmt.Sprintf(text, args...)}
}
// records that the given node failed to be converted and includes an explanatory message
func (ctx *parseContext) newBadNode(failedNode mkparser.Node, message string, args ...interface{}) starlarkNode {
return &exprNode{ctx.newBadExpr(failedNode, message, args...)}
} }
func (ctx *parseContext) parseCompare(cond *mkparser.Directive) starlarkExpr { func (ctx *parseContext) parseCompare(cond *mkparser.Directive) starlarkExpr {
@@ -1730,29 +1686,34 @@ func (ctx *parseContext) parseMakeString(node mkparser.Node, mk *mkparser.MakeSt
// Handles the statements whose treatment is the same in all contexts: comment, // Handles the statements whose treatment is the same in all contexts: comment,
// assignment, variable (which is a macro call in reality) and all constructs that // assignment, variable (which is a macro call in reality) and all constructs that
// do not handle in any context ('define directive and any unrecognized stuff). // do not handle in any context ('define directive and any unrecognized stuff).
func (ctx *parseContext) handleSimpleStatement(node mkparser.Node) { func (ctx *parseContext) handleSimpleStatement(node mkparser.Node) []starlarkNode {
var result []starlarkNode
switch x := node.(type) { switch x := node.(type) {
case *mkparser.Comment: case *mkparser.Comment:
if !ctx.maybeHandleAnnotation(x) { if n, handled := ctx.maybeHandleAnnotation(x); handled && n != nil {
ctx.insertComment("#" + x.Comment) result = []starlarkNode{n}
} else if !handled {
result = []starlarkNode{&commentNode{strings.TrimSpace("#" + x.Comment)}}
} }
case *mkparser.Assignment: case *mkparser.Assignment:
ctx.handleAssignment(x) result = ctx.handleAssignment(x)
case *mkparser.Variable: case *mkparser.Variable:
ctx.handleVariable(x) result = ctx.handleVariable(x)
case *mkparser.Directive: case *mkparser.Directive:
switch x.Name { switch x.Name {
case "define": case "define":
ctx.handleDefine(x) if res := ctx.maybeHandleDefine(x); res != nil {
result = []starlarkNode{res}
}
case "include", "-include": case "include", "-include":
ctx.handleInclude(node, ctx.parseMakeString(node, x.Args), x.Name[0] != '-') result = ctx.handleInclude(node, ctx.parseMakeString(node, x.Args), x.Name[0] != '-')
case "ifeq", "ifneq", "ifdef", "ifndef": case "ifeq", "ifneq", "ifdef", "ifndef":
ctx.handleIfBlock(x) result = []starlarkNode{ctx.handleIfBlock(x)}
default: default:
ctx.errorf(x, "unexpected directive %s", x.Name) result = []starlarkNode{ctx.newBadNode(x, "unexpected directive %s", x.Name)}
} }
default: default:
ctx.errorf(x, "unsupported line %s", strings.ReplaceAll(x.Dump(), "\n", "\n#")) result = []starlarkNode{ctx.newBadNode(x, "unsupported line %s", strings.ReplaceAll(x.Dump(), "\n", "\n#"))}
} }
// Clear the includeTops after each non-comment statement // Clear the includeTops after each non-comment statement
@@ -1761,12 +1722,17 @@ func (ctx *parseContext) handleSimpleStatement(node mkparser.Node) {
if _, wasComment := node.(*mkparser.Comment); !wasComment && len(ctx.includeTops) > 0 { if _, wasComment := node.(*mkparser.Comment); !wasComment && len(ctx.includeTops) > 0 {
ctx.includeTops = []string{} ctx.includeTops = []string{}
} }
if result == nil {
result = []starlarkNode{}
}
return result
} }
// Processes annotation. An annotation is a comment that starts with #RBC# and provides // Processes annotation. An annotation is a comment that starts with #RBC# and provides
// a conversion hint -- say, where to look for the dynamically calculated inherit/include // a conversion hint -- say, where to look for the dynamically calculated inherit/include
// paths. Returns true if the comment was a successfully-handled annotation. // paths. Returns true if the comment was a successfully-handled annotation.
func (ctx *parseContext) maybeHandleAnnotation(cnode *mkparser.Comment) bool { func (ctx *parseContext) maybeHandleAnnotation(cnode *mkparser.Comment) (starlarkNode, bool) {
maybeTrim := func(s, prefix string) (string, bool) { maybeTrim := func(s, prefix string) (string, bool) {
if strings.HasPrefix(s, prefix) { if strings.HasPrefix(s, prefix) {
return strings.TrimSpace(strings.TrimPrefix(s, prefix)), true return strings.TrimSpace(strings.TrimPrefix(s, prefix)), true
@@ -1775,44 +1741,20 @@ func (ctx *parseContext) maybeHandleAnnotation(cnode *mkparser.Comment) bool {
} }
annotation, ok := maybeTrim(cnode.Comment, annotationCommentPrefix) annotation, ok := maybeTrim(cnode.Comment, annotationCommentPrefix)
if !ok { if !ok {
return false return nil, false
} }
if p, ok := maybeTrim(annotation, "include_top"); ok { if p, ok := maybeTrim(annotation, "include_top"); ok {
// Don't allow duplicate include tops, because then we will generate // Don't allow duplicate include tops, because then we will generate
// invalid starlark code. (duplicate keys in the _entry dictionary) // invalid starlark code. (duplicate keys in the _entry dictionary)
for _, top := range ctx.includeTops { for _, top := range ctx.includeTops {
if top == p { if top == p {
return true return nil, true
} }
} }
ctx.includeTops = append(ctx.includeTops, p) ctx.includeTops = append(ctx.includeTops, p)
return true return nil, true
} }
ctx.errorf(cnode, "unsupported annotation %s", cnode.Comment) return ctx.newBadNode(cnode, "unsupported annotation %s", cnode.Comment), true
return true
}
func (ctx *parseContext) insertComment(s string) {
ctx.receiver.newNode(&commentNode{strings.TrimSpace(s)})
}
func (ctx *parseContext) carryAsComment(failedNode mkparser.Node) {
for _, line := range strings.Split(failedNode.Dump(), "\n") {
ctx.insertComment("# " + line)
}
}
// records that the given node failed to be converted and includes an explanatory message
func (ctx *parseContext) errorf(failedNode mkparser.Node, message string, args ...interface{}) {
if ctx.errorLogger != nil {
ctx.errorLogger.NewError(ctx.errorLocation(failedNode), failedNode, message, args...)
}
ctx.receiver.newNode(&exprNode{ctx.newBadExpr(failedNode, message, args...)})
ctx.script.hasErrors = true
}
func (ctx *parseContext) wrapBadExpr(xBad *badExpr) {
ctx.receiver.newNode(&exprNode{xBad})
} }
func (ctx *parseContext) loadedModulePath(path string) string { func (ctx *parseContext) loadedModulePath(path string) string {
@@ -1927,6 +1869,7 @@ func Convert(req Request) (*StarlarkScript, error) {
sourceFS: req.SourceFS, sourceFS: req.SourceFS,
makefileFinder: req.MakefileFinder, makefileFinder: req.MakefileFinder,
nodeLocator: func(pos mkparser.Pos) int { return parser.Unpack(pos).Line }, nodeLocator: func(pos mkparser.Pos) int { return parser.Unpack(pos).Line },
nodes: make([]starlarkNode, 0),
} }
ctx := newParseContext(starScript, nodes) ctx := newParseContext(starScript, nodes)
ctx.outputSuffix = req.OutputSuffix ctx.outputSuffix = req.OutputSuffix
@@ -1938,9 +1881,8 @@ func Convert(req Request) (*StarlarkScript, error) {
ctx.tracedVariables[v] = true ctx.tracedVariables[v] = true
} }
} }
ctx.pushReceiver(starScript)
for ctx.hasNodes() && ctx.fatalError == nil { for ctx.hasNodes() && ctx.fatalError == nil {
ctx.handleSimpleStatement(ctx.getNode()) starScript.nodes = append(starScript.nodes, ctx.handleSimpleStatement(ctx.getNode())...)
} }
if ctx.fatalError != nil { if ctx.fatalError != nil {
return nil, ctx.fatalError return nil, ctx.fatalError

View File

@@ -1153,7 +1153,6 @@ override FOO:=`,
def init(g, handle): def init(g, handle):
cfg = rblf.cfg(handle) cfg = rblf.cfg(handle)
rblf.mk2rbc_error("product.mk:2", "cannot handle override directive") rblf.mk2rbc_error("product.mk:2", "cannot handle override directive")
g["override FOO"] = ""
`, `,
}, },
{ {

View File

@@ -255,29 +255,17 @@ type switchCase struct {
nodes []starlarkNode nodes []starlarkNode
} }
func (cb *switchCase) newNode(node starlarkNode) {
cb.nodes = append(cb.nodes, node)
}
func (cb *switchCase) emit(gctx *generationContext) { func (cb *switchCase) emit(gctx *generationContext) {
cb.gate.emit(gctx) cb.gate.emit(gctx)
gctx.indentLevel++ gctx.indentLevel++
hasStatements := false hasStatements := false
emitNode := func(node starlarkNode) { for _, node := range cb.nodes {
if _, ok := node.(*commentNode); !ok { if _, ok := node.(*commentNode); !ok {
hasStatements = true hasStatements = true
} }
node.emit(gctx) node.emit(gctx)
} }
if len(cb.nodes) > 0 { if !hasStatements {
emitNode(cb.nodes[0])
for _, node := range cb.nodes[1:] {
emitNode(node)
}
if !hasStatements {
gctx.emitPass()
}
} else {
gctx.emitPass() gctx.emitPass()
} }
gctx.indentLevel-- gctx.indentLevel--
@@ -288,22 +276,8 @@ type switchNode struct {
ssCases []*switchCase ssCases []*switchCase
} }
func (ssw *switchNode) newNode(node starlarkNode) {
switch br := node.(type) {
case *switchCase:
ssw.ssCases = append(ssw.ssCases, br)
default:
panic(fmt.Errorf("expected switchCase node, got %t", br))
}
}
func (ssw *switchNode) emit(gctx *generationContext) { func (ssw *switchNode) emit(gctx *generationContext) {
if len(ssw.ssCases) == 0 { for _, ssCase := range ssw.ssCases {
gctx.emitPass() ssCase.emit(gctx)
} else {
ssw.ssCases[0].emit(gctx)
for _, ssCase := range ssw.ssCases[1:] {
ssCase.emit(gctx)
}
} }
} }