treewide: remove direct access to external/

This prepares the repositoriy to be compatible with the flag
--nolegacy_external_runfiles. This reduces runfiles & sandbox creation
 times.

Change-Id: I06720be4a3c873d68d8278dcb24271ed874f7134
Reviewed-on: https://review.monogon.dev/c/monogon/+/2747
Tested-by: Jenkins CI
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
diff --git a/build/toolbase/gotoolchain/BUILD.bazel b/build/toolbase/gotoolchain/BUILD.bazel
index 42dc877..1842398 100644
--- a/build/toolbase/gotoolchain/BUILD.bazel
+++ b/build/toolbase/gotoolchain/BUILD.bazel
@@ -6,7 +6,7 @@
     importpath = "source.monogon.dev/build/toolbase/gotoolchain",
     visibility = ["//visibility:public"],
     deps = [
-        "@io_bazel_rules_go//go/tools/bazel:go_default_library",
+        "@io_bazel_rules_go//go/runfiles:go_default_library",
     ],
 )
 
diff --git a/build/toolbase/gotoolchain/toolchain.go.in b/build/toolbase/gotoolchain/toolchain.go.in
index 2989f21..4923e67 100644
--- a/build/toolbase/gotoolchain/toolchain.go.in
+++ b/build/toolbase/gotoolchain/toolchain.go.in
@@ -4,37 +4,24 @@
 
 import (
 	"fmt"
-	"os"
+	"path/filepath"
 	"strings"
 
-	"github.com/bazelbuild/rules_go/go/tools/bazel"
+	"github.com/bazelbuild/rules_go/go/runfiles"
 )
 
 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)
+	// TODO(tim): I couldn't find out why there is a prefix.
+	s = strings.TrimPrefix(s, "external/")
+	res, err := runfiles.Rlocation(s)
 	if err != nil {
 		panic(fmt.Sprintf("runfile %q not found: %v", s, err))
 	}
-	return res
+	abs, err := filepath.Abs(res)
+	if err != nil {
+		panic(fmt.Sprintf("cant make runfile %q absolute: %v", s, err))
+	}
+	return abs
 }
 
 var (
diff --git a/metropolis/installer/test/main.go b/metropolis/installer/test/main.go
index 4bcf5d0..56b1128 100644
--- a/metropolis/installer/test/main.go
+++ b/metropolis/installer/test/main.go
@@ -57,19 +57,31 @@
 // QEMU is killed shortly after the string is found, or when the context is
 // cancelled.
 func runQemu(ctx context.Context, args []string, expectedOutput string) (bool, error) {
+	ovmfVarsPath, err := runfiles.Rlocation("edk2/OVMF_VARS.fd")
+	if err != nil {
+		return false, err
+	}
+	ovmfCodePath, err := runfiles.Rlocation("edk2/OVMF_CODE.fd")
+	if err != nil {
+		return false, err
+	}
+	qemuPath, err := runfiles.Rlocation("qemu/qemu-x86_64-softmmu")
+	if err != nil {
+		return false, err
+	}
 	defaultArgs := []string{
 		"-machine", "q35", "-accel", "kvm", "-nographic", "-nodefaults",
 		"-m", "512",
 		"-smp", "2",
 		"-cpu", "host",
-		"-drive", "if=pflash,format=raw,readonly=on,file=external/edk2/OVMF_CODE.fd",
-		"-drive", "if=pflash,format=raw,snapshot=on,file=external/edk2/OVMF_VARS.fd",
+		"-drive", "if=pflash,format=raw,snapshot=on,file=" + ovmfCodePath,
+		"-drive", "if=pflash,format=raw,readonly=on,file=" + ovmfVarsPath,
 		"-serial", "stdio",
 		"-no-reboot",
 	}
 	qemuArgs := append(defaultArgs, args...)
 	pf := cmd.TerminateIfFound(expectedOutput, nil)
-	return cmd.RunCommand(ctx, "external/qemu/qemu-x86_64-softmmu", qemuArgs, pf)
+	return cmd.RunCommand(ctx, qemuPath, qemuArgs, pf)
 }
 
 // runQemuWithInstaller runs the Metropolis Installer in a qemu, performing the
diff --git a/metropolis/test/ktest/ktest.bzl b/metropolis/test/ktest/ktest.bzl
index f0f0eba..8cb5257 100644
--- a/metropolis/test/ktest/ktest.bzl
+++ b/metropolis/test/ktest/ktest.bzl
@@ -54,6 +54,5 @@
             "//metropolis/test/ktest",
             ":test_initramfs",
             "//metropolis/test/ktest:linux-testing",
-            "@com_github_bonzini_qboot//:qboot-bin",
         ],
     )
diff --git a/metropolis/test/launch/BUILD.bazel b/metropolis/test/launch/BUILD.bazel
index 91c3b3d..cc5ef6c 100644
--- a/metropolis/test/launch/BUILD.bazel
+++ b/metropolis/test/launch/BUILD.bazel
@@ -6,10 +6,14 @@
         "launch.go",
         "log.go",
     ],
+    data = [
+        "@com_github_bonzini_qboot//:qboot-bin",
+    ],
     importpath = "source.monogon.dev/metropolis/test/launch",
     visibility = ["//metropolis:__subpackages__"],
     deps = [
         "//metropolis/pkg/freeport",
+        "@io_bazel_rules_go//go/runfiles:go_default_library",
         "@org_golang_x_sys//unix",
     ],
 )
diff --git a/metropolis/test/launch/cluster/cluster.go b/metropolis/test/launch/cluster/cluster.go
index 06d13da..69d9667 100644
--- a/metropolis/test/launch/cluster/cluster.go
+++ b/metropolis/test/launch/cluster/cluster.go
@@ -276,13 +276,18 @@
 		options.Mac = mac
 	}
 
+	ovmfCodePath, err := runfiles.Rlocation("edk2/OVMF_CODE.fd")
+	if err != nil {
+		return err
+	}
+
 	tpmSocketPath := filepath.Join(r.sd, "tpm-socket")
 	fwVarPath := filepath.Join(r.ld, "OVMF_VARS.fd")
 	storagePath := filepath.Join(r.ld, "image.img")
 	qemuArgs := []string{
 		"-machine", "q35", "-accel", "kvm", "-nographic", "-nodefaults", "-m", "4096",
 		"-cpu", "host", "-smp", "sockets=1,cpus=1,cores=2,threads=2,maxcpus=4",
-		"-drive", "if=pflash,format=raw,readonly=on,file=external/edk2/OVMF_CODE.fd",
+		"-drive", "if=pflash,format=raw,readonly=on,file=" + ovmfCodePath,
 		"-drive", "if=pflash,format=raw,file=" + fwVarPath,
 		"-drive", "if=virtio,format=raw,cache=unsafe,file=" + storagePath,
 		"-netdev", qemuNetConfig.ToOption(qemuNetType),
@@ -332,7 +337,7 @@
 	tpmEmuCmd.Stderr = os.Stderr
 	tpmEmuCmd.Stdout = os.Stdout
 
-	err := tpmEmuCmd.Start()
+	err = tpmEmuCmd.Start()
 	if err != nil {
 		return fmt.Errorf("failed to start TPM emulator: %w", err)
 	}
diff --git a/metropolis/test/launch/launch.go b/metropolis/test/launch/launch.go
index a048cef..93e45a6 100644
--- a/metropolis/test/launch/launch.go
+++ b/metropolis/test/launch/launch.go
@@ -30,6 +30,7 @@
 	"strings"
 	"syscall"
 
+	"github.com/bazelbuild/rules_go/go/runfiles"
 	"golang.org/x/sys/unix"
 
 	"source.monogon.dev/metropolis/pkg/freeport"
@@ -245,13 +246,18 @@
 	// kernel, initramfs and command line are mapped into VM memory at boot time and
 	// not loaded from any sort of disk. Booting and shutting off one of these VMs
 	// takes <100ms.
+	biosPath, err := runfiles.Rlocation("com_github_bonzini_qboot/bios.bin")
+	if err != nil {
+		return fmt.Errorf("while searching bios: %w", err)
+	}
+
 	baseArgs := []string{
 		"-nodefaults", "-no-user-config", "-nographic", "-no-reboot",
 		"-accel", "kvm", "-cpu", "host",
 		"-m", "1G",
 		// Needed until QEMU updates their bundled qboot version (needs
 		// https://github.com/bonzini/qboot/pull/28)
-		"-bios", "external/com_github_bonzini_qboot/bios.bin",
+		"-bios", biosPath,
 		"-M", "microvm,x-option-roms=off,pic=off,pit=off,rtc=off,isa-serial=off",
 		"-kernel", opts.KernelPath,
 		// We force using a triple-fault reboot strategy since otherwise the kernel first
@@ -303,7 +309,7 @@
 
 	PrettyPrintQemuArgs(opts.Name, cmd.Args)
 
-	err := cmd.Run()
+	err = cmd.Run()
 	// If it's a context error, just quit. There's no way to tell a
 	// killed-due-to-context vs killed-due-to-external-reason error returned by Run,
 	// so we approximate by looking at the context's status.
diff --git a/metropolis/vm/smoketest/BUILD.bazel b/metropolis/vm/smoketest/BUILD.bazel
index 1dce6e4..7264bcf 100644
--- a/metropolis/vm/smoketest/BUILD.bazel
+++ b/metropolis/vm/smoketest/BUILD.bazel
@@ -6,6 +6,7 @@
     srcs = ["main.go"],
     importpath = "source.monogon.dev/metropolis/vm/smoketest",
     visibility = ["//visibility:private"],
+    deps = ["@io_bazel_rules_go//go/runfiles:go_default_library"],
 )
 
 node_initramfs(
diff --git a/metropolis/vm/smoketest/main.go b/metropolis/vm/smoketest/main.go
index 103f4d3..d22a578 100644
--- a/metropolis/vm/smoketest/main.go
+++ b/metropolis/vm/smoketest/main.go
@@ -25,6 +25,8 @@
 	"net"
 	"os"
 	"os/exec"
+
+	"github.com/bazelbuild/rules_go/go/runfiles"
 )
 
 func main() {
@@ -47,11 +49,21 @@
 		}
 	}()
 
+	qemuPath, err := runfiles.Rlocation("qemu/qemu-x86_64-softmmu")
+	if err != nil {
+		panic(err)
+	}
+
+	// TODO(lorenz): This explicitly doesn't use our own qboot because it cannot be built in a musl configuration.
+	// This will be fixed once we have a proper multi-target toolchain.
+	biosPath, err := runfiles.Rlocation("qemu/pc-bios/qboot.rom")
+	if err != nil {
+		panic(err)
+	}
+
 	baseArgs := []string{"-nodefaults", "-no-user-config", "-nographic", "-no-reboot",
 		"-accel", "kvm", "-cpu", "host",
-		// TODO(lorenz): This explicitly doesn't use our own qboot because it cannot be built in a musl configuration.
-		// This will be fixed once we have a proper multi-target toolchain.
-		"-bios", "external/qemu/pc-bios/qboot.rom",
+		"-bios", biosPath,
 		"-M", "microvm,x-option-roms=off,pic=off,pit=off,rtc=off,isa-serial=off",
 		"-kernel", "metropolis/test/ktest/linux-testing.elf",
 		"-append", "reboot=t console=hvc0 quiet",
@@ -62,7 +74,7 @@
 		"-chardev", "socket,id=test,path=metropolis/vm/smoketest,abstract=on",
 		"-device", "virtserialport,chardev=test",
 	}
-	qemuCmd := exec.Command("external/qemu/qemu-x86_64-softmmu", baseArgs...)
+	qemuCmd := exec.Command(qemuPath, baseArgs...)
 	qemuCmd.Stdout = os.Stdout
 	qemuCmd.Stderr = os.Stderr
 	if err := qemuCmd.Run(); err != nil {