Allow variable-prefixed include statements in mk2rbc

mk2rbc was already searching the whole android tree
for Makefiles, so allowing variable-prefixed include
statements doesn't affect performance on the file searching
front. On the generated code front, there's already a cap
of 150 potentially-included makefiles that prevents the
performance from getting too bad, and it can be lowered
if necessary.

Bug: 213508006
Test: go test
Change-Id: I3a4e81acb3d97bee08ac3dbe63052a274acf5793
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index e317cad..ebb463b 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -829,11 +829,7 @@
 			pathPattern = append(pathPattern, chunk)
 		}
 	}
-	if pathPattern[0] == "" {
-		if len(ctx.includeTops) == 0 {
-			ctx.errorf(v, "inherit-product/include statements must not be prefixed with a variable, or must include a #RBC# include_top comment beforehand giving a root directory to search.")
-			return
-		}
+	if pathPattern[0] == "" && len(ctx.includeTops) > 0 {
 		// If pattern starts from the top. restrict it to the directories where
 		// we know inherit-product uses dynamically calculated path.
 		for _, p := range ctx.includeTops {
@@ -849,14 +845,20 @@
 		ctx.errorf(v, "there are >%d files matching the pattern, please rewrite it", maxMatchingFiles)
 		return
 	}
-	res := inheritedDynamicModule{*varPath, []*moduleInfo{}, loadAlways}
-	for _, p := range matchingPaths {
-		// A product configuration files discovered dynamically may attempt to inherit
-		// from another one which does not exist in this source tree. Prevent load errors
-		// by always loading the dynamic files as optional.
-		res.candidateModules = append(res.candidateModules, ctx.newDependentModule(p, true))
+	if len(matchingPaths) == 1 {
+		res := inheritedStaticModule{ctx.newDependentModule(matchingPaths[0], loadAlways && ctx.ifNestLevel == 0), loadAlways}
+		processModule(res)
+	} else {
+		needsWarning := pathPattern[0] == "" && len(ctx.includeTops) == 0
+		res := inheritedDynamicModule{*varPath, []*moduleInfo{}, loadAlways, ctx.errorLocation(v), needsWarning}
+		for _, p := range matchingPaths {
+			// A product configuration files discovered dynamically may attempt to inherit
+			// from another one which does not exist in this source tree. Prevent load errors
+			// by always loading the dynamic files as optional.
+			res.candidateModules = append(res.candidateModules, ctx.newDependentModule(p, true))
+		}
+		processModule(res)
 	}
-	processModule(res)
 }
 
 func (ctx *parseContext) findMatchingPaths(pattern []string) []string {
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index c4f7da3..20f198a 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -1072,13 +1072,7 @@
   cfg = rblf.cfg(handle)
   g["MY_PATH"] = "foo"
   #RBC# include_top vendor/foo1
-  _entry = {
-    "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
-  }.get("%s/cfg.mk" % g["MY_PATH"])
-  (_varmod, _varmod_init) = _entry if _entry else (None, None)
-  if not _varmod_init:
-    rblf.mkerror("product.mk", "Cannot find %s" % ("%s/cfg.mk" % g["MY_PATH"]))
-  rblf.inherit(handle, _varmod, _varmod_init)
+  rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
 `,
 	},
 	{
@@ -1098,25 +1092,13 @@
   cfg = rblf.cfg(handle)
   g["MY_PATH"] = "foo"
   #RBC# include_top vendor/foo1
-  _entry = {
-    "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
-  }.get("%s/cfg.mk" % g["MY_PATH"])
-  (_varmod, _varmod_init) = _entry if _entry else (None, None)
-  if not _varmod_init:
-    rblf.mkerror("product.mk", "Cannot find %s" % ("%s/cfg.mk" % g["MY_PATH"]))
-  rblf.inherit(handle, _varmod, _varmod_init)
+  rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
   #RBC# include_top vendor/foo1
-  _entry = {
-    "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
-  }.get("%s/cfg.mk" % g["MY_PATH"])
-  (_varmod, _varmod_init) = _entry if _entry else (None, None)
-  if not _varmod_init:
-    rblf.mkerror("product.mk", "Cannot find %s" % ("%s/cfg.mk" % g["MY_PATH"]))
-  rblf.inherit(handle, _varmod, _varmod_init)
+  rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
 `,
 	},
 	{
-		desc:   "Dynamic inherit path that lacks necessary hint",
+		desc:   "Dynamic inherit path that lacks hint",
 		mkname: "product.mk",
 		in: `
 #RBC# include_top foo
@@ -1133,26 +1115,23 @@
 		expected: `#RBC# include_top foo
 load("//build/make/core:product_config.rbc", "rblf")
 load("//foo:font.star|init", _font_init = "init")
+load("//bar:font.star|init", _font1_init = "init")
 
 def init(g, handle):
   cfg = rblf.cfg(handle)
-  _entry = {
-    "foo/font.mk": ("foo/font", _font_init),
-  }.get("%s/font.mk" % g.get("MY_VAR", ""))
-  (_varmod, _varmod_init) = _entry if _entry else (None, None)
-  if not _varmod_init:
-    rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", "")))
-  rblf.inherit(handle, _varmod, _varmod_init)
+  rblf.inherit(handle, "foo/font", _font_init)
   #RBC# include_top foo
   # There's some space and even this comment between the include_top and the inherit-product
+  rblf.inherit(handle, "foo/font", _font_init)
+  rblf.mkwarning("product.mk:11", "Including a path with a non-constant prefix, please convert this to a simple literal to generate cleaner starlark.")
   _entry = {
     "foo/font.mk": ("foo/font", _font_init),
+    "bar/font.mk": ("bar/font", _font1_init),
   }.get("%s/font.mk" % g.get("MY_VAR", ""))
   (_varmod, _varmod_init) = _entry if _entry else (None, None)
   if not _varmod_init:
     rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", "")))
   rblf.inherit(handle, _varmod, _varmod_init)
-  rblf.mk2rbc_error("product.mk:11", "inherit-product/include statements must not be prefixed with a variable, or must include a #RBC# include_top comment beforehand giving a root directory to search.")
 `,
 	},
 	{
diff --git a/mk2rbc/node.go b/mk2rbc/node.go
index 4f7c4f0..dea4dc8 100644
--- a/mk2rbc/node.go
+++ b/mk2rbc/node.go
@@ -86,6 +86,8 @@
 	path             interpolateExpr
 	candidateModules []*moduleInfo
 	loadAlways       bool
+	location         ErrorLocation
+	needsWarning     bool
 }
 
 func (i inheritedDynamicModule) name() string {
@@ -97,6 +99,10 @@
 }
 
 func (i inheritedDynamicModule) emitSelect(gctx *generationContext) {
+	if i.needsWarning {
+		gctx.newLine()
+		gctx.writef("%s.mkwarning(%q, %q)", baseName, i.location, "Including a path with a non-constant prefix, please convert this to a simple literal to generate cleaner starlark.")
+	}
 	gctx.newLine()
 	gctx.writef("_entry = {")
 	gctx.indentLevel++