core: build initramfs using generic initramfs rule

This chips away at three different things:
 - it brings us closer to hermetic and cross-platform builds by not
   depending on genrule/shell and lz4-the-tool
 - it generalizes initramfs building (allowing for more than one to be
   built, if necessary)
 - sets the stage to use Bazel transitions [1] to force all included Go
   binaries to be built in pure/static mode while allowing host Go
   binaries to use cgo/dynamic linking if necessary, and hopefully also
   allowing us to get rid of some BUILD patches that set pure='on' in
   go_binary calls (notably needed in Cilium and some existing
   third_party dependencies).

[1] - https://docs.bazel.build/versions/master/skylark/config.html#user-defined-transitions

Test Plan: build machinery change, covered by existing tests

X-Origin-Diff: phab/D554
GitOrigin-RevId: a5561eb5ca16e6529b9a4a2b98352f579c424222
diff --git a/build/savestdout/BUILD.bazel b/build/savestdout/BUILD.bazel
new file mode 100644
index 0000000..7208c6d
--- /dev/null
+++ b/build/savestdout/BUILD.bazel
@@ -0,0 +1,14 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
+
+go_library(
+    name = "go_default_library",
+    srcs = ["savestdout.go"],
+    importpath = "git.monogon.dev/source/nexantic.git/build/savestdout",
+    visibility = ["//visibility:private"],
+)
+
+go_binary(
+    name = "savestdout",
+    embed = [":go_default_library"],
+    visibility = ["//visibility:public"],
+)
diff --git a/build/savestdout/README.md b/build/savestdout/README.md
new file mode 100644
index 0000000..a233620
--- /dev/null
+++ b/build/savestdout/README.md
@@ -0,0 +1,22 @@
+savestdout
+==========
+
+`savestdout` is a small tool to save the stdout of a command to a file, without using
+a shell.
+
+It was made to be used in Bazel rule definitions that want to run a command and save
+its output to stdout without going through ctx.actions.run_shell.
+
+Once [bazelbuild/bazel/issues/5511](https://github.com/bazelbuild/bazel/issues/5511)
+gets fixed, rules that need this behaviour can start using native Bazel functionality
+instead, and this tool should be deleted.
+
+Usage
+-----
+
+Command line usage:
+
+    bazel build //build/savestdout
+    bazel run bazel-bin/build/savestdout/*/savestdout /tmp/foo ps aux
+
+For an example of use in rules, see `smalltown_initramfs` in `//code/def.bzl`.
diff --git a/build/savestdout/savestdout.go b/build/savestdout/savestdout.go
new file mode 100644
index 0000000..a0fb709
--- /dev/null
+++ b/build/savestdout/savestdout.go
@@ -0,0 +1,51 @@
+// Copyright 2020 The Monogon Project Authors.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"log"
+	"os"
+	"os/exec"
+)
+
+func main() {
+	if len(os.Args) < 3 {
+		log.Fatalf("Usage: %s output_file program <args...>", os.Args[0])
+	}
+
+	f, err := os.Create(os.Args[1])
+	if err != nil {
+		log.Fatalf("Create(%q): %v", os.Args[1], err)
+	}
+	defer f.Close()
+
+	args := os.Args[3:]
+	cmd := exec.Command(os.Args[2], args...)
+	cmd.Stderr = os.Stderr
+	cmd.Stdout = f
+
+	err = cmd.Run()
+	if err == nil {
+		return
+	}
+
+	if e, ok := err.(*exec.ExitError); ok {
+		os.Exit(e.ExitCode())
+	}
+
+	log.Fatalf("Could not start command: %v", err)
+}