Skip to main content

max / alloy

Add a c dial, so a machine can build a checkout it just cloned Ruled 2026-09-07 (Max), who put the goal as caring "more about being able to clone a repo and compile it from scratch than keeping the original compiler for the shipped binary". That framing is what makes this bounded, and it is why the row is the compilers and the build systems rather than a library set. Reproducing a shipped binary means matching its toolchain and carrying every header it linked, which is unbounded and is the "spread of -devel packages that no language toggle models" that killed the rebuild-what-we-ship rule. Building an arbitrary checkout means having gcc, g++, make, cmake, meson, ninja and the autotools, and letting the person layer whatever their project names. The second is a fixed cost: 193 MiB across 86 packages over a rust image, measured with dnf --assumeno on fedora-bootc:43. gcc is named in the arm as well as arriving with rust, so LANGS=c alone is a working toolchain rather than one that depends on another row being on. fw12, fw13 and astra all take it. The builder TUI gets the row with its cost in the label, the way the other toolchains carry theirs.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_01WFBzMprSmNCfvdj2cGZyka
Author: Max Johnson <me@maxj.phd> · 2026-09-07 18:09 UTC
Signed with PGP, not checked
Commit: 36ef051c2d9ca3d11fcc557cdde3a6d9288baf51
Parent: 5112c5e
5 files changed, +47 insertions, -6 deletions
M Containerfile +33 -2
@@ -1030,8 +1030,8 @@
1030 1030 esac; \
1031 1031 for lang in $(echo "$LANGS" | tr ',' ' '); do \
1032 1032 case "$lang" in \
1033 - rust|go|python|zig|js) ;; \
1034 - *) echo "unknown language '$lang'; the builder offers rust, go, python, zig and js" >&2; exit 1 ;; \
1033 + rust|c|go|python|zig|js) ;; \
1034 + *) echo "unknown language '$lang'; the builder offers rust, c, go, python, zig and js" >&2; exit 1 ;; \
1035 1035 esac; \
1036 1036 done; \
1037 1037 case "$DB" in \
@@ -2114,6 +2114,28 @@
2114 2114 # default is profile-blind for the same reason — a client image has no
2115 2115 # more claim on a compiler than a server one.
2116 2116 #
2117 + # WHAT `c` IS, because it is not a language row like the others. It is the
2118 + # toolchain somebody needs to clone a repository and build it: gcc and g++,
2119 + # make, cmake, meson and ninja, and the autotools trio. Ruled 2026-09-07
2120 + # (Max), who put the goal as caring "more about being able to clone a repo
2121 + # and compile it from scratch than keeping the original compiler for the
2122 + # shipped binary".
2123 + #
2124 + # That framing is what keeps this bounded. Reproducing a shipped binary means
2125 + # matching its toolchain and carrying every library header it linked, which is
2126 + # unbounded and is the "spread of -devel packages that no language toggle
2127 + # models" that killed the rebuild-what-we-ship rule. Building an arbitrary
2128 + # checkout means having the compilers and the build systems, and letting the
2129 + # person layer whatever that project names. The second is a fixed cost and
2130 + # this row is it: 193 MiB across 86 packages over a rust image, measured with
2131 + # `dnf install --assumeno` on fedora-bootc:43, 2026-09-07.
2132 + #
2133 + # gcc is named here as well as arriving with rust, so `LANGS=c` alone is a
2134 + # working C toolchain rather than one that depends on another row being on.
2135 + #
2136 + # Weak dependencies off, matching every other install in this file. cmake
2137 + # recommends its full documentation set and nothing here reads it.
2138 + #
2117 2139 # THREE -devel PACKAGES RIDE WITH rust, and they are not the language's.
2118 2140 # wayland-devel, libxkbcommon-devel and fontconfig-devel are what shop
2119 2141 # links against, and shop is the terminal this image ships and runs. The
@@ -2142,6 +2164,9 @@
2142 2164 case "$lang" in \
2143 2165 rust) dnf install -y rust cargo \
2144 2166 wayland-devel libxkbcommon-devel fontconfig-devel ;; \
2167 + c) dnf install -y --setopt=install_weak_deps=False \
2168 + gcc gcc-c++ make cmake meson ninja-build \
2169 + autoconf automake libtool patch pkgconf ;; \
2145 2170 go) dnf install -y golang ;; \
2146 2171 python) dnf install -y python3 python3-pip ;; \
2147 2172 zig) dnf install -y zig ;; \
@@ -2156,6 +2181,12 @@
2156 2181 pkg-config --exists "$mod" \
2157 2182 || { echo "rust was asked for and $mod has no pkgconfig file; shop cannot be rebuilt on this image" >&2; exit 1; }; \
2158 2183 done ;; \
2184 + c) for t in gcc g++ make cmake meson; do \
2185 + command -v "$t" >/dev/null \
2186 + || { echo "c was asked for and $t is not in the image" >&2; exit 1; }; \
2187 + done; \
2188 + command -v ninja >/dev/null || command -v ninja-build >/dev/null \
2189 + || { echo "c was asked for and ninja is not in the image" >&2; exit 1; } ;; \
2159 2190 go) command -v go >/dev/null || { echo "go was asked for and the go binary is not in the image" >&2; exit 1; } ;; \
2160 2191 python) command -v python3 >/dev/null || { echo "python was asked for and python3 is not in the image" >&2; exit 1; } ;; \
2161 2192 zig) command -v zig >/dev/null || { echo "zig was asked for and the zig binary is not in the image" >&2; exit 1; } ;; \
@@ -8,7 +8,7 @@
8 8
9 9 # A build host is a mint-time choice rather than a property of the profile:
10 10 # astra builds the aarch64 images, runs the sweep, and hosts PoM's runner.
11 - LANGS=rust,js,go
11 + LANGS=rust,c,js,go
12 12
13 13 # Same reasoning as fw13: PoM's runner and the CI scratch databases want the
14 14 # major production runs, not the one Fedora ships.
@@ -48,7 +48,7 @@
48 48 # machines can already build it when somebody uses it. 356 MB. It also covers
49 49 # the Go tier of what this image ships, which is syncthing, restic, tailscale,
50 50 # direnv, fzf, gopass and cliphist.
51 - LANGS=rust,go
51 + LANGS=rust,c,go
52 52
53 53 # fw13's public key, read at mint time rather than committed: this repo is
54 54 # public, and a key pasted into it would also go stale silently. The installer
@@ -9,7 +9,7 @@
9 9 # with `npm run build` before it creates a database or boots anything, and that
10 10 # is the one place a frontend failure is fatal. The C toolchain arrives with
11 11 # rust: a mint without it has no gcc, no binutils and no make.
12 - LANGS=rust,js,go
12 + LANGS=rust,c,js,go
13 13
14 14 # Sando's migration_dry_run and cargo_test gates restore production dumps into
15 15 # a local scratch cluster over a unix socket. Production is PostgreSQL 16, so a
@@ -253,6 +253,7 @@
253 253 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
254 254 pub(crate) enum Lang {
255 255 Rust,
256 + C,
256 257 Go,
257 258 Python,
258 259 Zig,
@@ -260,11 +261,19 @@
260 261 }
261 262
262 263 impl Lang {
263 - const ALL: [Lang; 5] = [Lang::Rust, Lang::Go, Lang::Python, Lang::Zig, Lang::Js];
264 + const ALL: [Lang; 6] = [
265 + Lang::Rust,
266 + Lang::C,
267 + Lang::Go,
268 + Lang::Python,
269 + Lang::Zig,
270 + Lang::Js,
271 + ];
264 272
265 273 const fn value(self) -> &'static str {
266 274 match self {
267 275 Lang::Rust => "rust",
276 + Lang::C => "c",
268 277 Lang::Go => "go",
269 278 Lang::Python => "python",
270 279 Lang::Zig => "zig",
@@ -284,6 +293,7 @@
284 293 const fn label(self) -> &'static str {
285 294 match self {
286 295 Lang::Rust => "rust (610 MiB; the build-host role asks for this)",
296 + Lang::C => "c (193 MiB; g++, make, cmake, meson, ninja, autotools)",
287 297 Lang::Go => "go (356 MB; rebuilds syncthing, restic, tailscale)",
288 298 Lang::Python => "python",
289 299 Lang::Zig => "zig",