treewide: remove usage of global tags between build configs

Change-Id: Ie7c4714d30f8c3342a97451d58e14cfb43087586
Reviewed-on: https://review.monogon.dev/c/monogon/+/3776
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
Tested-by: Jenkins CI
diff --git a/.bazelrc b/.bazelrc
index 1dcf729..1527f42 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -18,6 +18,12 @@
 # Set compilation mode (-c) to debug when running with --config dbg.
 build:dbg --compilation_mode=dbg
 
+# Run race config with race detector
+build:race --@io_bazel_rules_go//go/config:race
+
+# Force netgo and osusergo
+build --@io_bazel_rules_go//go/config:tags=osusergo,netgo
+
 # Run all spawns in our own hermetic sandbox sysroot.
 build --experimental_use_hermetic_linux_sandbox
 build --sandbox_fake_hostname
@@ -71,26 +77,6 @@
 # Build resources
 startup --batch_cpu_scheduling --io_nice_level 7
 
-# selinux:
-#     build with SELinux (containerd, kubelet)
-# no_zfs,no_aufs,no_devicemapper:
-#     disable containerd features we don't need
-# providerless,dockerless:
-#     build k8s without cloud provider and docker support
-# nowasm:
-#     disable wasm plugin support in sqlc
-# nobtrfs,nozfs,notapestats,norapl:
-#     disable node_exporter features we don't need
-build --@io_bazel_rules_go//go/config:tags=selinux,seccomp,no_zfs,no_aufs,no_devicemapper,providerless,dockerless,nowasm,netgo,osusergo,nobtrfs,nozfs,notapestats,norapl
-
-# kvm_debug:
-#     prevent stackoverflows for gvisor
-build:dbg --@io_bazel_rules_go//go/config:tags=selinux,seccomp,no_zfs,no_aufs,no_devicemapper,providerless,dockerless,nowasm,netgo,osusergo,nobtrfs,nozfs,notapestats,norapl,kvm_debug
-
-# Run race config with race detector
-build:race --@io_bazel_rules_go//go/config:race
-build:race --@io_bazel_rules_go//go/config:tags=selinux,seccomp,no_zfs,no_aufs,no_devicemapper,providerless,dockerless,nowasm,netgo,osusergo,nobtrfs,nozfs,notapestats,norapl,race
-
 # Build with C++17.
 build --cxxopt=-std=c++17
 
diff --git a/build/bazel/go.MODULE.bazel b/build/bazel/go.MODULE.bazel
index 9d8c2b6..415842d 100644
--- a/build/bazel/go.MODULE.bazel
+++ b/build/bazel/go.MODULE.bazel
@@ -110,7 +110,7 @@
     "github.com/containerd/containerd/v2": {
         "directives": [
             "gazelle:proto disable",
-            "gazelle:build_tags no_btrfs,no_zfs",
+            "gazelle:build_tags no_aufs,no_btrfs,no_devmapper,no_zfs",
         ],
     },
     "github.com/containerd/cgroups/v3": {
@@ -160,11 +160,6 @@
             "//third_party/go/patches:runc-add-cdeps.patch",
         ],
     },
-    "github.com/prometheus/node_exporter": {
-        "directives": [
-            "gazelle:build_tags nobtrfs,nozfs,notapestats,norapl",
-        ],
-    },
     "github.com/google/cadvisor": {
         "directives": [
             "gazelle:proto disable_global",
@@ -174,11 +169,9 @@
         ],
     },
     "github.com/sqlc-dev/sqlc": {
-        "build_extra_args": [
-            "-exclude=internal/ext/wasm/wasm.go",
-        ],
         "directives": [
             "gazelle:proto disable",
+            "gazelle:build_tags nowasm",
         ],
     },
     "github.com/containerd/ttrpc": {
@@ -252,7 +245,6 @@
     "k8s.io/kubernetes": {
         "directives": [
             "gazelle:proto disable",
-            "gazelle:build_tags providerless,dockerless",
         ],
         "patches": [
             "//third_party/go/patches:k8s-native-metrics.patch",
diff --git a/build/go/BUILD.bazel b/build/go/BUILD.bazel
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build/go/BUILD.bazel
diff --git a/build/go/def.bzl b/build/go/def.bzl
new file mode 100644
index 0000000..c696564
--- /dev/null
+++ b/build/go/def.bzl
@@ -0,0 +1,67 @@
+load("@bazel_skylib//lib:paths.bzl", "paths")
+
+def _build_with_tag_transition_impl(settings, attr):
+    """
+    Transition that enables pure, static build of Go binaries.
+    """
+    tags = settings["@io_bazel_rules_go//go/config:tags"]
+
+    return {
+        "@io_bazel_rules_go//go/config:tags": tags + attr.gotags,
+    }
+
+build_with_tag_transition = transition(
+    implementation = _build_with_tag_transition_impl,
+    inputs = [
+        "@io_bazel_rules_go//go/config:tags",
+    ],
+    outputs = [
+        "@io_bazel_rules_go//go/config:tags",
+    ],
+)
+
+def _go_binary_with_tag_impl(ctx):
+    # We need to forward the DefaultInfo provider from the underlying rule.
+    # Unfortunately, we can't do this directly, because Bazel requires that the executable to run
+    # is actually generated by this rule, so we need to symlink to it, and generate a synthetic
+    # forwarding DefaultInfo.
+
+    result = []
+    binary = ctx.attr.binary[0]
+
+    default_info = binary[DefaultInfo]
+    new_executable = None
+    original_executable = default_info.files_to_run.executable
+
+    if not original_executable:
+        fail("Cannot transition a 'binary' that is not executable")
+
+    # In order for the symlink to have the same basename as the original
+    # executable (important in the case of proto plugins), put it in a
+    # subdirectory named after the label to prevent collisions.
+    new_executable = ctx.actions.declare_file(paths.join(ctx.label.name, original_executable.basename))
+    ctx.actions.symlink(
+        output = new_executable,
+        target_file = original_executable,
+        is_executable = True,
+    )
+
+    result.append(
+        DefaultInfo(
+            files = depset(direct = [new_executable]),
+            executable = new_executable,
+        ),
+    )
+
+    return result
+
+go_binary_with_tag = rule(
+    implementation = _go_binary_with_tag_impl,
+    attrs = {
+        "binary": attr.label(
+            mandatory = True,
+            cfg = build_with_tag_transition,
+        ),
+        "gotags": attr.string_list(),
+    },
+)
diff --git a/build/sqlc/BUILD.bazel b/build/sqlc/BUILD.bazel
index e69de29..63fd6ff 100644
--- a/build/sqlc/BUILD.bazel
+++ b/build/sqlc/BUILD.bazel
@@ -0,0 +1,7 @@
+load("//build/go:def.bzl", "go_binary_with_tag")
+
+go_binary_with_tag(
+    name = "sqlc",
+    binary = "@com_github_sqlc_dev_sqlc//cmd/sqlc",
+    gotags = ["nowasm"],
+)
diff --git a/build/sqlc/sqlc.bzl b/build/sqlc/sqlc.bzl
index 985a301..88b2ec5 100644
--- a/build/sqlc/sqlc.bzl
+++ b/build/sqlc/sqlc.bzl
@@ -152,7 +152,7 @@
             values = ["postgresql", "cockroachdb"],
         ),
         "_sqlc": attr.label(
-            default = Label("@com_github_sqlc_dev_sqlc//cmd/sqlc"),
+            default = Label(":sqlc"),
             allow_single_file = True,
             executable = True,
             cfg = "exec",
diff --git a/metropolis/node/BUILD.bazel b/metropolis/node/BUILD.bazel
index 1638083..d04db8b 100644
--- a/metropolis/node/BUILD.bazel
+++ b/metropolis/node/BUILD.bazel
@@ -1,5 +1,6 @@
 load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
 load("@rules_pkg//:pkg.bzl", "pkg_zip")
+load("//build/go:def.bzl", "go_binary_with_tag")
 load("//osbase/build:def.bzl", "erofs_image", "verity_image")
 load("//osbase/build:efi.bzl", "efi_unified_kernel_image")
 load("//osbase/build/genosrelease:defs.bzl", "os_release")
@@ -35,6 +36,12 @@
     "passwd",
 ])
 
+go_binary_with_tag(
+    name = "runc",
+    binary = "@com_github_opencontainers_runc//:runc",
+    gotags = ["seccomp"],
+)
+
 erofs_image(
     name = "rootfs",
     files = {
@@ -85,7 +92,7 @@
     files_cc = {
         "//metropolis/node/core/minit": "/init",
         # runc runtime, with cgo
-        "@com_github_opencontainers_runc//:runc": "/containerd/bin/runc",
+        ":runc": "/containerd/bin/runc",
         "@xfsprogs//:mkfs": "/bin/mkfs.xfs",
         "@chrony//:chrony": "/time/chrony",
     },