Fix missing genrule srcs and tools with ALLOW_MISSING_DEPENDENCIES=true

Set the location label for missing srcs and tools to avoid
nonsensical errors when parsing the command.

Test: genrule_test.go
Test: paths_test.go
Test: unbundled branch with missing framework-res module needed by robolectric genrule
Change-Id: I9c1f1cd82a80f048c0e903b8e93910b1ae34b0b1
diff --git a/genrule/genrule.go b/genrule/genrule.go
index e259b1d..87e6747 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -189,6 +189,8 @@
 	}
 
 	if len(g.properties.Tools) > 0 {
+		seenTools := make(map[string]bool)
+
 		ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
 			switch tag := ctx.OtherModuleDependencyTag(module).(type) {
 			case hostToolDependencyTag:
@@ -220,11 +222,25 @@
 				if path.Valid() {
 					g.deps = append(g.deps, path.Path())
 					addLocationLabel(tag.label, []string{path.Path().String()})
+					seenTools[tag.label] = true
 				} else {
 					ctx.ModuleErrorf("host tool %q missing output file", tool)
 				}
 			}
 		})
+
+		// If AllowMissingDependencies is enabled, the build will not have stopped when
+		// AddFarVariationDependencies was called on a missing tool, which will result in nonsensical
+		// "cmd: unknown location label ..." errors later.  Add a dummy file to the local label.  The
+		// command that uses this dummy file will never be executed because the rule will be replaced with
+		// an android.Error rule reporting the missing dependencies.
+		if ctx.Config().AllowMissingDependencies() {
+			for _, tool := range g.properties.Tools {
+				if !seenTools[tool] {
+					addLocationLabel(tool, []string{"***missing tool " + tool + "***"})
+				}
+			}
+		}
 	}
 
 	if ctx.Failed() {
@@ -239,9 +255,24 @@
 
 	var srcFiles android.Paths
 	for _, in := range g.properties.Srcs {
-		paths := android.PathsForModuleSrcExcludes(ctx, []string{in}, g.properties.Exclude_srcs)
-		srcFiles = append(srcFiles, paths...)
-		addLocationLabel(in, paths.Strings())
+		paths, missingDeps := android.PathsAndMissingDepsForModuleSrcExcludes(ctx, []string{in}, g.properties.Exclude_srcs)
+		if len(missingDeps) > 0 {
+			if !ctx.Config().AllowMissingDependencies() {
+				panic(fmt.Errorf("should never get here, the missing dependencies %q should have been reported in DepsMutator",
+					missingDeps))
+			}
+
+			// If AllowMissingDependencies is enabled, the build will not have stopped when
+			// the dependency was added on a missing SourceFileProducer module, which will result in nonsensical
+			// "cmd: label ":..." has no files" errors later.  Add a dummy file to the local label.  The
+			// command that uses this dummy file will never be executed because the rule will be replaced with
+			// an android.Error rule reporting the missing dependencies.
+			ctx.AddMissingDependencies(missingDeps)
+			addLocationLabel(in, []string{"***missing srcs " + in + "***"})
+		} else {
+			srcFiles = append(srcFiles, paths...)
+			addLocationLabel(in, paths.Strings())
+		}
 	}
 
 	task := g.taskGenerator(ctx, String(g.properties.Cmd), srcFiles)
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 5cb51b8..0b6952f 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -17,11 +17,13 @@
 import (
 	"io/ioutil"
 	"os"
+	"reflect"
 	"strings"
 	"testing"
 
 	"android/soong/android"
-	"reflect"
+
+	"github.com/google/blueprint/proptools"
 )
 
 var buildDir string
@@ -123,6 +125,8 @@
 		name string
 		prop string
 
+		allowMissingDependencies bool
+
 		err    string
 		expect string
 	}{
@@ -425,6 +429,30 @@
 			`,
 			err: "must have at least one output file",
 		},
+		{
+			name: "srcs allow missing dependencies",
+			prop: `
+				srcs: [":missing"],
+				out: ["out"],
+				cmd: "cat $(location :missing) > $(out)",
+			`,
+
+			allowMissingDependencies: true,
+
+			expect: "cat ***missing srcs :missing*** > __SBOX_OUT_FILES__",
+		},
+		{
+			name: "tool allow missing dependencies",
+			prop: `
+				tools: [":missing"],
+				out: ["out"],
+				cmd: "$(location :missing) > $(out)",
+			`,
+
+			allowMissingDependencies: true,
+
+			expect: "***missing tool :missing*** > __SBOX_OUT_FILES__",
+		},
 	}
 
 	for _, test := range testcases {
@@ -435,7 +463,10 @@
 			bp += test.prop
 			bp += "}\n"
 
+			config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(test.allowMissingDependencies)
+
 			ctx := testContext(config, bp, nil)
+			ctx.SetAllowMissingDependencies(test.allowMissingDependencies)
 
 			_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 			if errs == nil {
@@ -460,8 +491,8 @@
 			}
 
 			gen := ctx.ModuleForTests("gen", "").Module().(*Module)
-			if gen.rawCommand != "'"+test.expect+"'" {
-				t.Errorf("want %q, got %q", test.expect, gen.rawCommand)
+			if g, w := gen.rawCommand, "'"+test.expect+"'"; w != g {
+				t.Errorf("want %q, got %q", w, g)
 			}
 		})
 	}