third_party/nix: introduce toolchain bundle

This introduces a nix derivation that builds a musl amd64/aarch64
toolchain sysroot.

Change-Id: Iba082edb8fd1f2ab580020bb1c7339a76487f3c8
Reviewed-on: https://review.monogon.dev/c/monogon/+/4006
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
Tested-by: Jenkins CI
diff --git a/build/toolchain/README.md b/build/toolchain/README.md
new file mode 100644
index 0000000..99e673f
--- /dev/null
+++ b/build/toolchain/README.md
@@ -0,0 +1,11 @@
+# Toolchain Bundle (`toolchain-bundle/`)
+
+To ensure that tools like `make`, `nasm`, `qemu`, or `perl` are available in the Bazel build environment, we provide a `toolchain-bundle`. This bundle is pre-built and fetched as an external repository, allowing Bazel to use these tools without needing to install them on the host system. They are built for both `x86_64-unknown-linux-musl` and `aarch64-unknown-linux-musl` platforms with Nix.
+
+You can build these toolchains by invoking the `nix-build` via `nix-build build/toolchain/toolchain-bundle/default.nix`
+
+---
+
+# Rust EFI Toolchain (`rust-efi/`)
+
+The `rust-efi` directory configures a Rust toolchain for building EFI applications.
\ No newline at end of file
diff --git a/build/toolchain/toolchain-bundle/BUILD.bazel b/build/toolchain/toolchain-bundle/BUILD.bazel
new file mode 100644
index 0000000..1b26331
--- /dev/null
+++ b/build/toolchain/toolchain-bundle/BUILD.bazel
@@ -0,0 +1,11 @@
+load(":toolchain.bzl", "TOOLCHAINS", "toolchain_for")
+
+package(default_visibility = ["//visibility:public"])
+
+[
+    toolchain_for(
+        name = name,
+        config = config,
+    )
+    for name, config in TOOLCHAINS.items()
+]
diff --git a/build/toolchain/toolchain-bundle/default.nix b/build/toolchain/toolchain-bundle/default.nix
new file mode 100644
index 0000000..f59a6a8
--- /dev/null
+++ b/build/toolchain/toolchain-bundle/default.nix
@@ -0,0 +1,52 @@
+{ pkgs ? import ../../../third_party/nix/default.nix { } }: with pkgs;
+symlinkJoin {
+  name = "toolchain";
+  paths =
+    let
+      platforms = with pkgsCross; [
+        aarch64-multiplatform-musl
+        musl64
+      ];
+    in
+    map
+      (platform: (
+        let
+          merged = buildEnv {
+            name = "toolchain-env";
+            paths = with platform.pkgsStatic; [
+              gnumake
+              flex
+              bison
+              lz4
+              busybox
+              findutils
+              bc
+              util-linux-minimal # custom pkg
+              perl
+              nasm
+              acpica-tools
+              patch
+              diffutils
+              qemu-minimal # custom pkg
+              m4
+              strace
+            ];
+          };
+        in
+        stdenv.mkDerivation rec {
+          name = "toolchain-bundle";
+          buildInputs = [ gnutar zstd ];
+
+          phases = [ "buildPhase" "installPhase" ];
+          buildPhase = ''
+            tar --zstd --sort=name --hard-dereference -hcf bundle.tar.zst -C ${merged} .
+          '';
+
+          installPhase = ''
+            mkdir $out
+            mv bundle.tar.zst $out/${name}-${platform.hostPlatform.config}-${lib.version}.tar.zst
+          '';
+        }
+      ))
+      platforms;
+}
diff --git a/build/toolchain/toolchain-bundle/toolchain-bundle.bzl b/build/toolchain/toolchain-bundle/toolchain-bundle.bzl
new file mode 100644
index 0000000..6ac17fb
--- /dev/null
+++ b/build/toolchain/toolchain-bundle/toolchain-bundle.bzl
@@ -0,0 +1,453 @@
+package(default_visibility = ["//visibility:public"])
+
+exports_files(glob([
+    "**/*",
+]))
+
+# rules_perl expects all files as src entry, this does prevent us using
+# $(execpath) which is why we have another filegroup that uses this as
+# data dep.
+filegroup(
+    name = "perl_runtime",
+    srcs = [
+        ":bin/perl",
+        ":bin/xsubpp",
+    ] + glob([
+        "lib/perl5/**",
+    ]),
+)
+
+filegroup(
+    name = "perl",
+    srcs = [
+        ":bin/perl",
+    ],
+    data = [
+        ":perl_runtime",
+    ],
+)
+
+filegroup(
+    name = "bison",
+    srcs = [
+        ":bin/bison",
+    ],
+    data = glob([
+        "share/bison/**",
+    ]),
+)
+
+filegroup(
+    name = "qemu-kvm",
+    srcs = [
+        ":bin/qemu-kvm",
+    ],
+    data = glob([
+        "share/qemu-firmware/**",
+        "share/qemu/**",
+    ]),
+)
+
+filegroup(
+    name = "busybox",
+    srcs = [
+        ":bin/busybox",
+    ],
+    data = [
+        ":bin/[",
+        ":bin/[[",
+        ":bin/acpid",
+        ":bin/add-shell",
+        ":bin/addgroup",
+        ":bin/adduser",
+        ":bin/adjtimex",
+        ":bin/arch",
+        ":bin/arp",
+        ":bin/arping",
+        ":bin/ascii",
+        ":bin/ash",
+        ":bin/awk",
+        ":bin/base32",
+        ":bin/base64",
+        ":bin/basename",
+        ":bin/bc",
+        ":bin/beep",
+        ":bin/blkdiscard",
+        ":bin/blkid",
+        ":bin/blockdev",
+        ":bin/bootchartd",
+        ":bin/brctl",
+        ":bin/bunzip2",
+        ":bin/busybox",
+        ":bin/bzcat",
+        ":bin/bzip2",
+        ":bin/cal",
+        ":bin/cat",
+        ":bin/chat",
+        ":bin/chattr",
+        ":bin/chgrp",
+        ":bin/chmod",
+        ":bin/chown",
+        ":bin/chpasswd",
+        ":bin/chpst",
+        ":bin/chroot",
+        ":bin/chrt",
+        ":bin/chvt",
+        ":bin/cksum",
+        ":bin/clear",
+        ":bin/cmp",
+        ":bin/comm",
+        ":bin/conspy",
+        ":bin/cp",
+        ":bin/cpio",
+        ":bin/crc32",
+        ":bin/crond",
+        ":bin/crontab",
+        ":bin/cryptpw",
+        ":bin/cttyhack",
+        ":bin/cut",
+        ":bin/date",
+        ":bin/dc",
+        ":bin/dd",
+        ":bin/deallocvt",
+        ":bin/delgroup",
+        ":bin/deluser",
+        ":bin/depmod",
+        ":bin/devmem",
+        ":bin/df",
+        ":bin/dhcprelay",
+        ":bin/diff",
+        ":bin/dirname",
+        ":bin/dmesg",
+        ":bin/dnsd",
+        ":bin/dnsdomainname",
+        ":bin/dos2unix",
+        ":bin/dpkg",
+        ":bin/dpkg-deb",
+        ":bin/du",
+        ":bin/dumpkmap",
+        ":bin/dumpleases",
+        ":bin/echo",
+        ":bin/ed",
+        ":bin/egrep",
+        ":bin/eject",
+        ":bin/env",
+        ":bin/envdir",
+        ":bin/envuidgid",
+        ":bin/ether-wake",
+        ":bin/expand",
+        ":bin/expr",
+        ":bin/factor",
+        ":bin/fakeidentd",
+        ":bin/fallocate",
+        ":bin/false",
+        ":bin/fatattr",
+        ":bin/fbset",
+        ":bin/fbsplash",
+        ":bin/fdflush",
+        ":bin/fdformat",
+        ":bin/fdisk",
+        ":bin/fgconsole",
+        ":bin/fgrep",
+        ":bin/find",
+        ":bin/findfs",
+        ":bin/flock",
+        ":bin/fold",
+        ":bin/free",
+        ":bin/freeramdisk",
+        ":bin/fsck",
+        ":bin/fsck.minix",
+        ":bin/fsfreeze",
+        ":bin/fstrim",
+        ":bin/fsync",
+        ":bin/ftpd",
+        ":bin/ftpget",
+        ":bin/ftpput",
+        ":bin/fuser",
+        ":bin/getopt",
+        ":bin/getty",
+        ":bin/grep",
+        ":bin/groups",
+        ":bin/gunzip",
+        ":bin/gzip",
+        ":bin/halt",
+        ":bin/hd",
+        ":bin/hdparm",
+        ":bin/head",
+        ":bin/hexdump",
+        ":bin/hexedit",
+        ":bin/hostid",
+        ":bin/hostname",
+        ":bin/httpd",
+        ":bin/hush",
+        ":bin/hwclock",
+        ":bin/i2cdetect",
+        ":bin/i2cdump",
+        ":bin/i2cget",
+        ":bin/i2cset",
+        ":bin/i2ctransfer",
+        ":bin/id",
+        ":bin/ifconfig",
+        ":bin/ifdown",
+        ":bin/ifenslave",
+        ":bin/ifplugd",
+        ":bin/ifup",
+        ":bin/inetd",
+        ":bin/init",
+        ":bin/insmod",
+        ":bin/install",
+        ":bin/ionice",
+        ":bin/iostat",
+        ":bin/ip",
+        ":bin/ipaddr",
+        ":bin/ipcalc",
+        ":bin/ipcrm",
+        ":bin/ipcs",
+        ":bin/iplink",
+        ":bin/ipneigh",
+        ":bin/iproute",
+        ":bin/iprule",
+        ":bin/iptunnel",
+        ":bin/kbd_mode",
+        ":bin/kill",
+        ":bin/killall",
+        ":bin/killall5",
+        ":bin/klogd",
+        ":bin/less",
+        ":bin/link",
+        ":bin/linux32",
+        ":bin/linux64",
+        ":bin/ln",
+        ":bin/loadfont",
+        ":bin/loadkmap",
+        ":bin/logger",
+        ":bin/login",
+        ":bin/logname",
+        ":bin/logread",
+        ":bin/losetup",
+        ":bin/lpd",
+        ":bin/lpq",
+        ":bin/lpr",
+        ":bin/ls",
+        ":bin/lsattr",
+        ":bin/lsmod",
+        ":bin/lsof",
+        ":bin/lspci",
+        ":bin/lsscsi",
+        ":bin/lsusb",
+        ":bin/lzcat",
+        ":bin/lzma",
+        ":bin/lzop",
+        ":bin/makedevs",
+        ":bin/makemime",
+        ":bin/man",
+        ":bin/md5sum",
+        ":bin/mdev",
+        ":bin/mesg",
+        ":bin/microcom",
+        ":bin/mim",
+        ":bin/mkdir",
+        ":bin/mkdosfs",
+        ":bin/mke2fs",
+        ":bin/mkfifo",
+        ":bin/mkfs.ext2",
+        ":bin/mkfs.minix",
+        ":bin/mkfs.vfat",
+        ":bin/mknod",
+        ":bin/mkpasswd",
+        ":bin/mkswap",
+        ":bin/mktemp",
+        ":bin/modinfo",
+        ":bin/modprobe",
+        ":bin/more",
+        ":bin/mount",
+        ":bin/mountpoint",
+        ":bin/mpstat",
+        ":bin/mt",
+        ":bin/mv",
+        ":bin/nameif",
+        ":bin/nanddump",
+        ":bin/nandwrite",
+        ":bin/nbd-client",
+        ":bin/nc",
+        ":bin/netstat",
+        ":bin/nice",
+        ":bin/nl",
+        ":bin/nmeter",
+        ":bin/nohup",
+        ":bin/nologin",
+        ":bin/nproc",
+        ":bin/nsenter",
+        ":bin/nslookup",
+        ":bin/ntpd",
+        ":bin/od",
+        ":bin/openvt",
+        ":bin/partprobe",
+        ":bin/passwd",
+        ":bin/paste",
+        ":bin/patch",
+        ":bin/pgrep",
+        ":bin/pidof",
+        ":bin/ping",
+        ":bin/ping6",
+        ":bin/pipe_progress",
+        ":bin/pivot_root",
+        ":bin/pkill",
+        ":bin/pmap",
+        ":bin/popmaildir",
+        ":bin/poweroff",
+        ":bin/powertop",
+        ":bin/printenv",
+        ":bin/printf",
+        ":bin/ps",
+        ":bin/pscan",
+        ":bin/pstree",
+        ":bin/pwd",
+        ":bin/pwdx",
+        ":bin/raidautorun",
+        ":bin/rdate",
+        ":bin/rdev",
+        ":bin/readahead",
+        ":bin/readlink",
+        ":bin/readprofile",
+        ":bin/realpath",
+        ":bin/reboot",
+        ":bin/reformime",
+        ":bin/remove-shell",
+        ":bin/renice",
+        ":bin/reset",
+        ":bin/resize",
+        ":bin/resume",
+        ":bin/rev",
+        ":bin/rm",
+        ":bin/rmdir",
+        ":bin/rmmod",
+        ":bin/route",
+        ":bin/rpm",
+        ":bin/rpm2cpio",
+        ":bin/rtcwake",
+        ":bin/run-init",
+        ":bin/run-parts",
+        ":bin/runsv",
+        ":bin/runsvdir",
+        ":bin/rx",
+        ":bin/script",
+        ":bin/scriptreplay",
+        ":bin/sed",
+        ":bin/seedrng",
+        ":bin/sendmail",
+        ":bin/seq",
+        ":bin/setarch",
+        ":bin/setconsole",
+        ":bin/setfattr",
+        ":bin/setfont",
+        ":bin/setkeycodes",
+        ":bin/setlogcons",
+        ":bin/setpriv",
+        ":bin/setserial",
+        ":bin/setsid",
+        ":bin/setuidgid",
+        ":bin/sh",
+        ":bin/sha1sum",
+        ":bin/sha3sum",
+        ":bin/sha256sum",
+        ":bin/sha512sum",
+        ":bin/showkey",
+        ":bin/shred",
+        ":bin/shuf",
+        ":bin/slattach",
+        ":bin/sleep",
+        ":bin/smemcap",
+        ":bin/softlimit",
+        ":bin/sort",
+        ":bin/split",
+        ":bin/ssl_client",
+        ":bin/start-stop-daemon",
+        ":bin/stat",
+        ":bin/strings",
+        ":bin/stty",
+        ":bin/su",
+        ":bin/sulogin",
+        ":bin/sum",
+        ":bin/sv",
+        ":bin/svc",
+        ":bin/svlogd",
+        ":bin/svok",
+        ":bin/swapoff",
+        ":bin/swapon",
+        ":bin/switch_root",
+        ":bin/sync",
+        ":bin/sysctl",
+        ":bin/syslogd",
+        ":bin/tac",
+        ":bin/tail",
+        ":bin/tar",
+        ":bin/taskset",
+        ":bin/tcpsvd",
+        ":bin/tee",
+        ":bin/telnet",
+        ":bin/telnetd",
+        ":bin/test",
+        ":bin/tftp",
+        ":bin/tftpd",
+        ":bin/time",
+        ":bin/timeout",
+        ":bin/top",
+        ":bin/touch",
+        ":bin/tr",
+        ":bin/traceroute",
+        ":bin/traceroute6",
+        ":bin/tree",
+        ":bin/true",
+        ":bin/truncate",
+        ":bin/ts",
+        ":bin/tsort",
+        ":bin/tty",
+        ":bin/ttysize",
+        ":bin/tunctl",
+        ":bin/ubiattach",
+        ":bin/ubidetach",
+        ":bin/ubimkvol",
+        ":bin/ubirename",
+        ":bin/ubirmvol",
+        ":bin/ubirsvol",
+        ":bin/ubiupdatevol",
+        ":bin/udhcpc",
+        ":bin/udhcpc6",
+        ":bin/udhcpd",
+        ":bin/udpsvd",
+        ":bin/uevent",
+        ":bin/umount",
+        ":bin/uname",
+        ":bin/unexpand",
+        ":bin/uniq",
+        ":bin/unix2dos",
+        ":bin/unlink",
+        ":bin/unlzma",
+        ":bin/unshare",
+        ":bin/unxz",
+        ":bin/unzip",
+        ":bin/uptime",
+        ":bin/usleep",
+        ":bin/uudecode",
+        ":bin/uuencode",
+        ":bin/vconfig",
+        ":bin/vi",
+        ":bin/vlock",
+        ":bin/volname",
+        ":bin/watch",
+        ":bin/watchdog",
+        ":bin/wc",
+        ":bin/wget",
+        ":bin/which",
+        ":bin/whoami",
+        ":bin/whois",
+        ":bin/xargs",
+        ":bin/xxd",
+        ":bin/xz",
+        ":bin/xzcat",
+        ":bin/yes",
+        ":bin/zcat",
+        ":bin/zcip",
+    ],
+)
diff --git a/build/toolchain/toolchain-bundle/toolchain.bzl b/build/toolchain/toolchain-bundle/toolchain.bzl
new file mode 100644
index 0000000..a2b7df2
--- /dev/null
+++ b/build/toolchain/toolchain-bundle/toolchain.bzl
@@ -0,0 +1,188 @@
+load("@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl", "native_tool_toolchain")
+
+# Copied from bazel-contrib/rules_foreign_cc licensed under Apache-2.0
+def _current_toolchain_impl(ctx):
+    toolchain = ctx.toolchains[ctx.attr._toolchain]
+
+    if toolchain.data.target:
+        return [
+            toolchain,
+            platform_common.TemplateVariableInfo(toolchain.data.env),
+            DefaultInfo(
+                files = toolchain.data.target.files,
+                runfiles = toolchain.data.target.default_runfiles,
+            ),
+        ]
+    return [
+        toolchain,
+        platform_common.TemplateVariableInfo(toolchain.data.env),
+        DefaultInfo(),
+    ]
+
+def current_toolchain(name):
+    return rule(
+        implementation = _current_toolchain_impl,
+        attrs = {
+            "_toolchain": attr.string(default = "//build/toolchain/toolchain-bundle:%s_toolchain" % name),
+        },
+        toolchains = [
+            "//build/toolchain/toolchain-bundle:%s_toolchain" % name,
+        ],
+    )
+
+def toolchain_for(name, config):
+    native.toolchain_type(
+        name = "%s_toolchain" % name,
+    )
+
+    config.current_toolchain_func(
+        name = name,
+    )
+
+    native.toolchain(
+        name = "%s_linux_x86_64_toolchain" % name,
+        exec_compatible_with = [
+            "@platforms//os:linux",
+            "@platforms//cpu:x86_64",
+        ],
+        toolchain = ":%s_linux_x86_64" % name,
+        toolchain_type = ":%s_toolchain" % name,
+    )
+
+    native.toolchain(
+        name = "%s_linux_aarch64_toolchain" % name,
+        exec_compatible_with = [
+            "@platforms//os:linux",
+            "@platforms//cpu:aarch64",
+        ],
+        toolchain = ":%s_linux_aarch64" % name,
+        toolchain_type = ":%s_toolchain" % name,
+    )
+
+    native_tool_toolchain(
+        name = "%s_linux_aarch64" % name,
+        env = {
+            name.upper(): "$(execpath @toolchain-bundle-aarch64-unknown-linux-musl//:%s)" % config.target,
+        },
+        target = "@toolchain-bundle-aarch64-unknown-linux-musl//:%s" % config.target,
+    )
+
+    native_tool_toolchain(
+        name = "%s_linux_x86_64" % name,
+        env = {
+            name.upper(): "$(execpath @toolchain-bundle-x86_64-unknown-linux-musl//:%s)" % config.target,
+        },
+        target = "@toolchain-bundle-x86_64-unknown-linux-musl//:%s" % config.target,
+    )
+
+current_qemu_img_toolchain = current_toolchain("qemu-img")
+current_qemu_kvm_toolchain = current_toolchain("qemu-kvm")
+current_make_toolchain = current_toolchain("make")
+current_strace_toolchain = current_toolchain("strace")
+current_nasm_toolchain = current_toolchain("nasm")
+current_bison_toolchain = current_toolchain("bison")
+current_flex_toolchain = current_toolchain("flex")
+current_m4_toolchain = current_toolchain("m4")
+current_bc_toolchain = current_toolchain("bc")
+current_busybox_toolchain = current_toolchain("busybox")
+current_diff_toolchain = current_toolchain("diff")
+current_perl_toolchain = current_toolchain("perl")
+current_iasl_toolchain = current_toolchain("iasl")
+current_lz4_toolchain = current_toolchain("lz4")
+
+TOOLCHAINS = {
+    "qemu-img": struct(
+        target = "bin/qemu-img",
+        current_toolchain_func = current_qemu_img_toolchain,
+    ),
+    "qemu-kvm": struct(
+        target = "qemu-kvm",
+        current_toolchain_func = current_qemu_kvm_toolchain,
+    ),
+    "make": struct(
+        target = "bin/make",
+        current_toolchain_func = current_make_toolchain,
+    ),
+    "strace": struct(
+        target = "bin/strace",
+        current_toolchain_func = current_strace_toolchain,
+    ),
+    "nasm": struct(
+        target = "bin/nasm",
+        current_toolchain_func = current_nasm_toolchain,
+    ),
+    "bison": struct(
+        target = "bison",
+        current_toolchain_func = current_bison_toolchain,
+    ),
+    "flex": struct(
+        target = "bin/flex",
+        current_toolchain_func = current_flex_toolchain,
+    ),
+    "m4": struct(
+        target = "bin/m4",
+        current_toolchain_func = current_m4_toolchain,
+    ),
+    "bc": struct(
+        target = "bin/bc",
+        current_toolchain_func = current_bc_toolchain,
+    ),
+    "diff": struct(
+        target = "bin/diff",
+        current_toolchain_func = current_diff_toolchain,
+    ),
+    "iasl": struct(
+        target = "bin/iasl",
+        current_toolchain_func = current_iasl_toolchain,
+    ),
+    "busybox": struct(
+        target = "busybox",
+        current_toolchain_func = current_busybox_toolchain,
+    ),
+    "perl": struct(
+        target = "perl",
+        current_toolchain_func = current_perl_toolchain,
+    ),
+    "lz4": struct(
+        target = "bin/lz4",
+        current_toolchain_func = current_lz4_toolchain,
+    ),
+}
+
+def build_toolchain_env(ctx, toolchains):
+    toolchain_info = [ctx.toolchains[t] for t in toolchains]
+    env = dict([(k, v) for t in toolchain_info for k, v in t.data.env.items()])
+    env = env | {"TOOL_PATH": ":".join([t.data.target.files.to_list()[0].path.rsplit("/", 1)[0] for t in toolchain_info])}
+
+    inputs = depset(transitive = [
+        depset(transitive = [t.data.target.files, t.data.target.default_runfiles.files])
+        for t in toolchain_info
+    ])
+
+    return env, inputs
+
+TOOLCHAIN_ENV_SETUP = """
+set -e
+
+# Iterate over all environment variables and expand paths that are
+# either external or bazel-out.
+for name in $(env | cut -d= -f1); do
+  val="${!name}"
+  [[ "$val" != *external/* && "$val" != *bazel-out/* ]] && continue # Quick skip
+
+  sep=' '; [[ $name == "TOOL_PATH" ]] && sep=':' # Set separator: : for PATH, space otherwise
+  IFS=$sep read -r -a items <<< "$val"     # Split value into array using correct separator
+
+  for i in "${!items[@]}"; do
+    key="${items[i]%%=*}"; v="${items[i]#*=}" # Handle 'key=val' and standalone paths
+    if [[ ( $v == external/* || $v == bazel-out/* ) && -e "$v" ]]; then
+      [ "$key" = "$v" ] && items[i]=$(realpath -s "$v") || items[i]="$key=$(realpath -s "$v")"
+    fi
+  done
+  export "$name=$(IFS=$sep; echo "${items[*]}")" # Re-export with correct separator
+done
+
+# Add our now expanded TOOL_PATH to PATH
+PATH="$PATH:$TOOL_PATH"
+
+"""