build: gotoolchain: support runfiles in tools

This allows us to use gotoolchain.{Go,Root} at build time in genrules,
and in tests. I'm still somewhat confused as to why bazel.Runfile
doesn't do this by itself.

Change-Id: I3c4c236bbe9782c5aa01cf7b801f691bf1ac2d85
Reviewed-on: https://review.monogon.dev/c/monogon/+/491
Reviewed-by: Leopold Schabel <leo@nexantic.com>
diff --git a/build/toolbase/gotoolchain/toolchain.go.in b/build/toolbase/gotoolchain/toolchain.go.in
index ca3db65..2989f21 100644
--- a/build/toolbase/gotoolchain/toolchain.go.in
+++ b/build/toolbase/gotoolchain/toolchain.go.in
@@ -4,11 +4,32 @@
 
 import (
 	"fmt"
+	"os"
+	"strings"
 
 	"github.com/bazelbuild/rules_go/go/tools/bazel"
 )
 
 func mustRunfile(s string) string {
+    // When running as a tool (in a genrule, or a test, etc.), bazel.Runfile
+    // does not work. However, ${0}.runfiles/$runfile should be present. If so,
+    // return early and return that. Otherwise, carry on with bazel.Runfile.
+    //
+    // TODO(q3k): dig deeper into this and unify with //metropolis/cli/pkg/datafile.
+
+    // Ignore the error, worst case we get an empty string that will make a
+    // garbage path that won't point to a file.
+    ex, _ := os.Executable()
+    rf := ex + ".runfiles"
+    if _, err := os.Stat(rf); err == nil {
+        parts := strings.Split(s, "/")
+        parts[0] = rf
+        rf = strings.Join(parts, "/")
+        if _, err := os.Stat(rf); err == nil {
+            return rf
+        }
+    }
+
 	res, err := bazel.Runfile(s)
 	if err != nil {
 		panic(fmt.Sprintf("runfile %q not found: %v", s, err))