build: add an incremental GUI type-check via the nix devShell

Verifying a GUI change meant `nix build .#syn-gui`, which starts from a clean
sandbox every time: crane caches the ~600 dependencies, but the workspace
crates rebuild from scratch in release, then a 107 MB binary is copied back
from the remote builder. Fifteen minutes to learn whether a one-line change
type-checks.

The devShell already had the Rust toolchain but none of the GUI's native
libraries — those were declared only inside the syn-gui derivation — so a
plain `cargo check -p gui-app` there died on fontconfig or alsa, which is
what forced the hermetic build in the first place.

Factors those into a shared `guiBuildInputs` used by both syn-gui and the
devShell, adds pkg-config and bindgenHook (for mupdf-sys's build.rs), and
sets LD_LIBRARY_PATH so a binary built in the shell can also run — gpui
dlopen()s wayland/X11/Vulkan/ALSA rather than linking them, which the
packaged build handles with an rpath instead.

`just check-gui-nix` then runs a normal incremental cargo check against
rust/target: measured 4m30s cold, 14s warm, against ~15 minutes for the
hermetic build. `build-gui-nix` stays for final verification and for
producing a binary you can actually run.

Worth noting the gnarly mupdf postPatch workaround in syn-gui does not apply
here: it exists only because crane vendors dependencies into the read-only
Nix store, and a devShell build uses the normal writable cargo registry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014qebJeotZq6FvrDeTDBzeT
This commit is contained in:
Greg Shuflin
2026-07-26 03:01:31 -07:00
co-authored by Claude Opus 5
parent 2525c1a461
commit ba8fe6d042
2 changed files with 52 additions and 11 deletions
+37 -11
View File
@@ -84,6 +84,22 @@
];
};
# Native libraries the gpui desktop app needs on top of `commonArgs`.
# Shared with `devShells.default` so a plain `cargo check -p gui-app`
# works there too — without these a local build dies on fontconfig or
# alsa, which is exactly the thing that forces a full hermetic rebuild
# just to type-check a GUI change.
guiBuildInputs = pkgs.lib.optionals pkgs.stdenv.isLinux [
pkgs.libX11
pkgs.libxcursor
pkgs.libxrandr
pkgs.libxi
pkgs.libGL
pkgs.alsa-lib
pkgs.vulkan-loader
pkgs.libxcb
];
# Build workspace deps once (shared across all binaries)
cargoArtifacts = craneLib.buildDepsOnly commonArgs;
@@ -137,16 +153,7 @@
pkgs.rustPlatform.bindgenHook
];
buildInputs = commonArgs.buildInputs ++ pkgs.lib.optionals pkgs.stdenv.isLinux [
pkgs.libX11
pkgs.libxcursor
pkgs.libxrandr
pkgs.libxi
pkgs.libGL
pkgs.alsa-lib
pkgs.vulkan-loader
pkgs.libxcb
];
buildInputs = commonArgs.buildInputs ++ guiBuildInputs;
# gpui needs to find wayland/X11/Vulkan/ALSA libs at runtime
postFixup = pkgs.lib.optionalString pkgs.stdenv.isLinux ''
@@ -186,6 +193,19 @@
};
devShells.default = pkgs.mkShell {
# `bindgenHook` sets LIBCLANG_PATH etc. for mupdf-sys's build.rs. It
# belongs in nativeBuildInputs so its setup hook actually runs.
nativeBuildInputs = [
pkgs.pkg-config
pkgs.rustPlatform.bindgenHook
];
# commonArgs.buildInputs + guiBuildInputs mirror what the `syn-gui`
# derivation links against, so `cargo build`/`cargo check` inside this
# shell finds the same native libraries the hermetic build does. The
# payoff is incremental compilation: the hermetic build starts from a
# clean sandbox every time, so type-checking a one-line GUI change
# costs a full release rebuild of the workspace.
buildInputs = [
buildApkCi
createReleaseCi
@@ -194,7 +214,13 @@
androidSdk
pkgs.jdk17
pkgs.just
];
] ++ commonArgs.buildInputs ++ guiBuildInputs;
# gpui dlopen()s wayland/X11/Vulkan/ALSA at runtime rather than
# linking them, so a binary built in this shell needs them on the
# library path to actually run (the packaged build patchelfs an rpath
# in instead).
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath (commonArgs.buildInputs ++ guiBuildInputs);
ANDROID_HOME = "${androidSdk}/libexec/android-sdk";
ANDROID_SDK_ROOT = "${androidSdk}/libexec/android-sdk";
+15
View File
@@ -199,6 +199,21 @@ build-gui-nix:
nix build .#syn-gui --max-jobs auto --cores 0
# Type-check the gpui desktop GUI inside the nix devShell.
#
# Prefer this over `build-gui-nix` while iterating: the hermetic build starts
# from a clean sandbox every time, so it rebuilds the whole workspace in
# release just to check a one-line change. This runs a normal incremental
# `cargo check` against `rust/target`, with the same native libraries the
# packaged build links against — seconds instead of minutes, after the first
# run warms the cache. Use `build-gui-nix` for final verification, or when you
# need a binary you can actually run.
[doc: "Type-check the gpui GUI incrementally in the nix devShell"]
[group: "build"]
check-gui-nix *args:
nix develop -c cargo check --manifest-path rust/gui-app/Cargo.toml {{args}}
# Run all formatters
[group: "format"]
fmt-all: fmt-kotlin fmt-rust