build: a nix shell, because the loader is the one thing gcc cannot build

make falls back to the GNU toolchain when clang is absent, and that works for
the kernel. It does not work for the UEFI loader: BOOTX64.EFI is PE/COFF and
wants clang plus lld-link, so a machine with only gcc gets a kernel that builds
and a loader that silently stays whatever it already was.

That is not hypothetical -- it is what left a stale boot splash on this tree for
half a day. The mask in include/logo.h was the new mark, the kernel drew it, and
the loader kept drawing one compiled months earlier because nothing could relink
it and nothing said so.

shell.nix declares everything docs/guides/building.md lists, and building.md
gains a NixOS section saying why using it matters rather than just how.

Verified: with clang 21.1.8 and LLD 21.1.8 the loader relinks and the splash
draws Shift in metal, screendumped from the framebuffer rather than inferred.

Pinned to the host's nixpkgs rather than a flake input, deliberately. This is a
convenience for a development machine, not a reproducibility claim; if it ever
needs to be the latter it wants a flake and should say so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2
This commit is contained in:
Nexoniarz 2026-09-12 18:31:34 +02:00
commit 6c42ab3b1a
3 changed files with 68 additions and 0 deletions

48
shell.nix Normal file
View file

@ -0,0 +1,48 @@
# A development shell with everything docs/guides/building.md asks for.
#
# nix-shell # then: make, make qemu, make iso
#
# Why this exists: the UEFI loader is the one part that genuinely cannot be
# built without clang and lld. `make` falls back to the GNU toolchain for the
# kernel, so a machine with only gcc gets a kernel that builds and a loader that
# silently stays whatever it was -- which is exactly the failure that left a
# stale boot splash on this tree for half a day.
#
# Pinned to whatever nixpkgs the host is on rather than to a flake input, on
# purpose: this is a convenience for one developer's machine, not a
# reproducibility claim. If it ever needs to be the latter it wants a flake.
{ pkgs ? import <nixpkgs> { } }:
pkgs.mkShell {
name = "schos";
nativeBuildInputs = with pkgs; [
# The cross-compiler, and the reason this file exists. clang targets
# bare-metal ELF, 16-bit code and PE/COFF from one install; lld-link is what
# produces BOOTX64.EFI.
clang
lld
llvm # llvm-objcopy, llvm-objdump
# The host compiler, for tools/ -- mkfs, mkschfs, the SchFS suites. Must be
# a real Unix compiler: those need <unistd.h> and <dirent.h>.
gcc
gnumake
perl # generates the interrupt vector table
python3 # boot logo, crash-screen QR, mklogo
qemu # make qemu, and every test harness
OVMF # firmware for the UEFI path; the Makefile searches for it
xorriso # make iso
mtools # ESP staging
curl gnutar # fetching ports
];
shellHook = ''
echo "schos: clang $(clang --version | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')," \
"$(qemu-system-x86_64 --version | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')"
echo " make | make qemu | make schfs-check | make iso"
'';
}