nandi/frqpublic Fork 0
9afdfa4b60d28ae8f9335cb9011809d631b8cdc4
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

flake.nix · 401 lines · 17.7 KBNix Blame HistoryRaw
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago1{
2 # frq is Jolt source, so "building" it is three things, not one:
3 #
4 # jolt the runtime that reads it (github:jolt-lang/jolt)
5 # jolt-native libvidya and libjoltmoq, in Rust (gitlab:nandithebull/jolt-native)
6 # frq this tree, with its deps resolved to store paths
7 #
8 # Jolt resolves deps.edn by running git at startup, which a build sandbox has
9 # no network for — so every dep is fetched by Nix instead and handed back as
10 # a :local/root through -Sdeps.
11 #
12 # nix build .#frq && ./result/bin/frq
13 #
14 # On a machine that is not NixOS the GL driver is the host's and the loader
15 # will not find it, so the window never opens ("GL display: argument does not
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago16 # name a valid config"). The launcher handles that itself: off NixOS it hands
17 # the process to nixGL, which puts the host's driver ahead of the store's.
18 # Nothing extra to type, and a distrobox/container Arch is the same case as
19 # a bare one.
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago20 description = "frq a freeq client in jolt";
21
22 inputs = {
23 nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
24 # Jolt's own flake declares `self.submodules`, which this Nix rejects when
25 # the flake is fetched through the github scheme — so take the source and
26 # build it here. `vendor/` is a submodule and the build needs it.
27 jolt-src = {
28 url = "git+https://github.com/jolt-lang/jolt?submodules=1";
29 flake = false;
30 };
31
Take the jolt-native ahead of the release, since a run can build it 36f6c77 nandi 17d ago32 # Ahead of v0.1.3, which is what deps.edn and the scripts/*.dotslash pins
33 # name — and deliberately: the pins are the last release, and this is what
34 # `just run` builds, so a change to jolt-native can be run before there is
35 # a release to fetch. The two meet again at `just bump`.
36 #
37 # Pinned all the same, and pinned to a rev: this input carries both halves of
Pin the native half to the release the rest of the tree names cb7f9f1 nandi 17d ago38 # glimmer-vidya — libvidya, and the Jolt side that binds it — so an
39 # unpinned `main` is a build whose native half is free to sit at a
40 # different commit from the tree that talks to it. It did, and what the
41 # drift cost was silence: the Jolt half sent a reaction pill's hover card
42 # to a libvidya with no handler for one, and the pill said nothing.
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago43 jolt-native = {
Take the jolt-native ahead of the release, since a run can build it 36f6c77 nandi 17d ago44 url = "git+https://gitlab.com/nandithebull/jolt-native?rev=a9f3673367a8f0d3dd7d6ab1d380484d1df795c1";
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago45 flake = false;
46 };
47
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago48 # Chez itself, because the APK needs a cross target nixpkgs does not
49 # build: frq's Scheme is compiled to an arm64 boot image, and that wants
50 # Chez's own `tarm64le` workarea — boot files, xpatch and libkernel.a.
51 # The version is the one the hand-built tree under ~/.cache used, and the
52 # submodules are not optional (zuo builds it, lz4 and zlib link into it).
53 # The fork jolt's own Android pin names, built here rather than fetched as
54 # a release binary: upstream reads the socket address out of `struct
55 # addrinfo` at glibc's offset, which on Bionic is `ai_canonname`, so an APK
56 # built with upstream cannot open a TLS connection at all. Only the boot
57 # image uses it; the desktop package still builds jolt-src.
58 jolt-android-src = {
59 url = "git+https://gitlab.com/nandithebull/jolt?rev=2b80d68d1f7a31ba92b208b3957e5fb555617ada&submodules=1";
60 flake = false;
61 };
62
63 chez-src = {
64 url = "git+https://github.com/cisco/ChezScheme?ref=refs/tags/v10.4.1&submodules=1";
65 flake = false;
66 };
67
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago68 # The sha deps.edn pins, on the fork with the reconciler fixes.
69 glimmer = {
70 url = "git+https://gitlab.com/nandithebull/glimmer?rev=399df371c790d690fb6e4560c3d4d7f838502857";
71 flake = false;
72 };
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago73
74 # Only ever used off NixOS, to put the host GL driver on the loader path.
75 nixgl = {
76 url = "github:nix-community/nixGL";
77 inputs.nixpkgs.follows = "nixpkgs";
78 };
Bundle the closure into one file the AppImage runtime mounts f82b93f nandi 18d ago79
80 # Wraps a closure into a single self-extracting file. Only the `appimage`
81 # output evaluates it.
82 nix-appimage = {
83 url = "github:ralismark/nix-appimage";
84 inputs.nixpkgs.follows = "nixpkgs";
85 };
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago86 };
87
Bundle the closure into one file the AppImage runtime mounts f82b93f nandi 18d ago88 outputs = { self, nixpkgs, jolt-src, jolt-native, glimmer, chez-src, jolt-android-src, nixgl, nix-appimage }:
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago89 let
90 systems = [ "x86_64-linux" "aarch64-linux" ];
91 forEachSystem = f:
92 nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
Run this tree on a native half the builders made a32699e nandi 17d ago93
94 # Mesa, despite the name: it covers Intel and AMD alike. The NVIDIA
95 # wrappers are the ones that need --impure (they read the host kernel
96 # module's version), which is why this only ever reaches for Intel.
Stop carrying the 32-bit GL stack and half of git 040f2c1 nandi 16d ago97 #
98 # Built from nixGL's default.nix rather than taken from its flake
99 # outputs, for the one argument the flake hardcodes on: `enable32bits`,
100 # which on x86_64 puts a second, i686 copy of mesa, its LLVM, and
101 # intel-media-driver into the wrapper. frq is 64-bit on both halves —
102 # the Rust cdylibs and the Chez runtime — so nothing here ever opens the
103 # 32-bit driver, and carrying it is most of the dev shell's closure.
104 nixGLFor = pkgs: (import nixgl {
105 inherit pkgs;
106 enable32bits = false;
107 }).nixGLIntel;
Run this tree on a native half the builders made a32699e nandi 17d ago108
109 # egui reaches for these with dlopen rather than linking them, so being
110 # in the cdylib's buildInputs is not enough — whatever starts frq has to
Follow the xorg renames, and stop wrapping nix in distrobox a400a00 nandi 16d ago111 # put them on the loader path itself. Without libx11 here, vidya reports
Run this tree on a native half the builders made a32699e nandi 17d ago112 # "X11 unavailable", falls back to Wayland, and winit refuses to build a
113 # second event loop after the failed first one.
114 #
115 # Out here rather than beside the package that first needed them: the
116 # dev shell starts frq too, on this tree's source rather than the store's
117 # copy of it, and a second copy of this list is a second chance for the
118 # two ways of running to disagree about what the window needs.
119 runtimeLibsFor = pkgs: with pkgs; [
120 libGL
121 libxkbcommon
122 wayland
Follow the xorg renames, and stop wrapping nix in distrobox a400a00 nandi 16d ago123 libx11
124 libxcursor
125 libxi
126 libxrandr
Run this tree on a native half the builders made a32699e nandi 17d ago127 vulkan-loader
128 ];
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago129 in
130 {
131 packages = forEachSystem (pkgs:
132 let
133 inherit (pkgs) lib;
134
Run this tree on a native half the builders made a32699e nandi 17d ago135 nixGL = nixGLFor pkgs;
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago136
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago137 # libvidya (the retained-tree ABI glimmer-vidya binds, on egui) and
138 # libjoltmoq (the AV media plane). One workspace, two cdylibs.
139 native = pkgs.rustPlatform.buildRustPackage {
140 pname = "jolt-native";
141 version = "0.1.0";
142 src = jolt-native;
143
144 cargoLock = {
145 lockFile = "${jolt-native}/Cargo.lock";
146 allowBuiltinFetchGit = true;
147 };
148
149 nativeBuildInputs = with pkgs; [
150 pkg-config
151 cmake
152 rustPlatform.bindgenHook
153 ];
154
155 buildInputs = with pkgs; [
156 alsa-lib
157 pipewire
158 openssl
159 libxkbcommon
160 wayland
161 libGL
162 ];
163
164 # Upstream's .cargo/config.toml drives the whole build through
165 # DotSlash: a pinned rustc, sccache, and zig as the C/C++ compiler
166 # and linker, each fetched from the network on first use. None of
167 # that survives a build sandbox, and none of it is needed when the
168 # toolchain comes from the store — so drop it and let stdenv's cc
169 # link (openh264 is C++, which stdenv covers too).
170 postPatch = ''
171 rm -f .cargo/config.toml
172 '';
173
174 # bindgen reads linux/videodev2.h directly; upstream points it at
175 # zig's bundled headers, which under Nix is just the kernel headers.
176 V4L2R_VIDEODEV2_H_PATH = "${pkgs.linuxHeaders}/include";
177
178 doCheck = false;
179
180 installPhase = ''
181 runHook preInstall
182 mkdir -p "$out/lib"
183 install -m644 target/*/release/*.so "$out/lib/"
184 runHook postInstall
185 '';
186 };
187
188 # Jolt itself: Clojure on Chez, built the way its own flake builds it.
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago189 # A function, because there are two of them — upstream for the
190 # desktop, and the Bionic-addrinfo fork for the boot image the APK
191 # carries. Nothing else about the build differs.
192 joltFrom = src: pkgs.stdenv.mkDerivation {
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago193 pname = "jolt";
194 version = "dev";
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago195 inherit src;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago196
197 strictDeps = true;
198 nativeBuildInputs = with pkgs; [ chez makeWrapper pkg-config xxd ];
199 buildInputs = with pkgs; [ lz4 zlib ncurses openssl libuuid ];
200
201 JOLT_VERSION = "dev";
202 dontConfigure = true;
203
204 buildPhase = ''
205 runHook preBuild
206 scheme --script host/chez/build-jolt.ss release target/release/jolt
207 runHook postBuild
208 '';
209
210 installPhase = ''
211 runHook preInstall
212 mkdir -p "$out/bin"
213 install -m755 target/release/jolt "$out/bin/jolt"
214 runHook postInstall
215 '';
216
217 # jolt.deps shells out to git and unzip, and jolt.mvn-http dlopens
Stop carrying the 32-bit GL stack and half of git 040f2c1 nandi 16d ago218 # OpenSSL through the JOLT_OPENSSL_LIBDIR seam. gitMinimal rather
219 # than git: all jolt.deps asks for is clone/fetch/rev-parse, and
220 # the full package carries Perl and Python for the subcommands
221 # written in them — a quarter of a gigabyte for git-send-email.
Read the clock in the reader's zone when /etc/localtime is a file bffe879 nandi 18d ago222 #
223 # TZDIR so a zone *name* resolves wherever this runs: frq.clock
224 # hands one to tzset, and glibc then looks for the tzfile under
225 # /usr/share/zoneinfo unless told otherwise — which a NixOS host
226 # does not have. The store's own tzdata is there on both kinds of
227 # machine. --set-default, so a TZDIR the user set still wins.
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago228 postFixup = ''
229 wrapProgram "$out/bin/jolt" \
Stop carrying the 32-bit GL stack and half of git 040f2c1 nandi 16d ago230 --prefix PATH : "${pkgs.lib.makeBinPath [ pkgs.gitMinimal pkgs.unzip ]}" \
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago231 --set-default JOLT_OPENSSL_LIBDIR "${pkgs.lib.makeLibraryPath [ pkgs.openssl ]}" \
Read the clock in the reader's zone when /etc/localtime is a file bffe879 nandi 18d ago232 --set-default TZDIR "${pkgs.tzdata}/share/zoneinfo" \
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago233 --set-default SSL_CERT_FILE "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
234 '';
235 };
236
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago237 joltRuntime = joltFrom jolt-src;
238 joltAndroid = joltFrom jolt-android-src;
239
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago240 # glimmer-vidya lives inside the jolt-native checkout, and its own
241 # deps.edn asks for glimmer by git — the top-level override below
242 # answers for both.
243 glimmerVidya = "${jolt-native}/jolt/glimmer-vidya";
244
Run this tree on a native half the builders made a32699e nandi 17d ago245 runtimeLibs = runtimeLibsFor pkgs;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago246
247 # The project as jolt sees it: source, deps.edn, nothing else.
248 frqSource = pkgs.runCommand "frq-source" { } ''
249 mkdir -p "$out"
250 cp -r ${self}/src ${self}/deps.edn "$out/"
251 '';
252
253 # Jolt resolves deps.edn from the working directory, so the launcher
254 # runs from the store copy. Its .jolt/cpcache write lands on a
255 # read-only directory and jolt treats that as a quiet cache miss, so
256 # the only cost is re-resolving the (already local) graph per start.
257 frqScript = pkgs.writeShellScript "frq" ''
258 export LD_LIBRARY_PATH="${native}/lib:${lib.makeLibraryPath runtimeLibs}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
259 cd ${frqSource}
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago260
261 # On NixOS the store's Mesa is the system's and the window opens.
262 # Anywhere else the real driver is the host's, so defer to nixGL
263 # it prepends the host driver, which has to win over ours.
264 runner=""
265 [ -e /run/current-system ] || runner="${nixGL}/bin/nixGLIntel"
266
267 exec ''${runner} ${joltRuntime}/bin/jolt \
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago268 -Sdeps '{:deps {jolt-lang/glimmer {:local/root "${glimmer}"} nandi/glimmer-vidya {:local/root "${glimmerVidya}"}}}' \
269 -M:frq "$@"
270 '';
271
272 frq = pkgs.runCommand "frq-0.1.0"
273 {
274 meta = {
275 description = "A freeq client in jolt";
276 mainProgram = "frq";
277 platforms = systems;
278 };
279 }
280 ''
281 mkdir -p "$out/bin"
282 ln -s ${frqScript} "$out/bin/frq"
283 '';
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago284 # --- Android ------------------------------------------------------
285 # The SDK and the NDK are Google's, which means unfree and a licence
286 # to accept — so this is its own import of nixpkgs rather than the
287 # `legacyPackages` everything above uses. Confined to the Android
288 # outputs: `nix build` of frq itself never evaluates it.
289 #
Build the APK as derivations only, and retire the buck2 graph 3bc3aaa nandi 16d ago290 # The NDK here is r29, which is the one the pinned libvidya was
291 # built with.
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago292 androidPkgs = import nixpkgs {
293 inherit (pkgs.stdenv.hostPlatform) system;
294 config = {
295 allowUnfree = true;
296 android_sdk.accept_license = true;
297 };
298 };
299
300 androidComposition = androidPkgs.androidenv.composeAndroidPackages {
301 buildToolsVersions = [ "36.0.0" ];
302 platformVersions = [ "36" ];
303 includeNDK = true;
304 };
305
306 android = import ./nix/android.nix {
307 inherit pkgs self chez-src jolt-native glimmer joltAndroid;
308 inherit (pkgs) lib;
309 androidSdk = androidComposition.androidsdk;
310 ndk = androidComposition.ndk-bundle;
311 };
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago312 in
313 {
314 inherit native frq;
315 jolt = joltRuntime;
316 default = frq;
Bundle the closure into one file the AppImage runtime mounts f82b93f nandi 18d ago317
Take bb from the flake, so a checkout needs nix and nothing else 270bee0 nandi 16d ago318 # The interpreter scripts/ is written in, named here so that
319 # scripts/bb can build it. Nothing else in this flake uses it: it is
320 # an output because a shell script cannot ask for `nixpkgs#babashka`
321 # at the version this tree pins, and `.#bb` is exactly that.
322 bb = pkgs.babashka;
323
Bundle the closure into one file the AppImage runtime mounts f82b93f nandi 18d ago324 # frq and everything it loads, squashed into one runnable file for
325 # hosts without Nix. The whole closure rides along — Mesa included,
326 # which is not waste: off NixOS the launcher goes through nixGL, and
327 # nixGL needs a store Mesa to put the host's driver in front of.
328 appimage =
329 nix-appimage.bundlers.${pkgs.stdenv.hostPlatform.system}.default frq;
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago330 }
331 # An APK is built by a linux-x86_64 NDK and a linux-x86_64 jolt, and
332 # Google ships no other; on aarch64 the Android outputs are simply
333 # absent rather than present and broken.
334 // lib.optionalAttrs (pkgs.stdenv.hostPlatform.system == "x86_64-linux") {
335 inherit (android) apk chezAndroid joltBoot libjoltapp;
336 apk-unsigned = android.apk-unsigned;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago337 });
338
Run this tree on a native half the builders made a32699e nandi 17d ago339 # Where `just run` runs, and — because entering it realises what it
340 # names — what builds the half of frq that is not this working tree.
341 #
342 # The two halves, and the split is the whole point of the shell. The frq
343 # source is the files on disk, uncommitted edits and all. Everything
344 # under it — jolt, glimmer, glimmer-vidya, both native objects — is the
345 # flake's, at the revs flake.lock names, so a run says what it ran
346 # against and both halves of glimmer-vidya move together. That is the
347 # drift the `jolt-native` input's comment is about, and a pin frq can
348 # answer for is worth more here than the convenience of a checkout.
349 #
350 # `native` is the cargo build of that input rather than jolt-native's own
351 # buck2 graph, which is a compromise and not a free one: buck2 is what
352 # its CI runs and what makes its releases, and its cpal has the pipewire
353 # feature this one does not, so device *names* in a call come out as ALSA
354 # PCMs. What it buys is a derivation — one thing nixbuild.net can be
355 # handed. The buck2 build fetches its rustc, zig and every third-party
356 # crate as it goes and writes buck-out into the tree it builds; a sandbox
357 # with no network and a read-only store is the one place it cannot run,
358 # so on a remote builder it is not a slower option but no option at all.
359 #
360 # Nothing here says "nixbuild", though: it is a plain derivation, and
361 # where it gets built is the machine's business. scripts/run.bb asks for
362 # the shell with --max-jobs 0, which is what sends it to the `builders`
363 # entry rather than compiling egui on a laptop.
364 devShells = forEachSystem (pkgs:
365 let
366 inherit (pkgs) lib;
367 inherit (self.packages.${pkgs.stdenv.hostPlatform.system}) jolt native;
368 in
369 {
370 default = pkgs.mkShellNoCC {
371 name = "frq";
372
373 # jolt, because the runtime frq is run by should be the flake's
374 # too. nixGL for the same reason the launcher reaches for it — see
Take bb from the flake, so a checkout needs nix and nothing else 270bee0 nandi 16d ago375 # frqScript. babashka because scripts/bb prefers one on PATH, and
376 # inside here that should be this one rather than a second copy
Take just from the dev shell, so the host needs no copy of it 9afdfa4 nandi 16d ago377 # built through `.#bb`. just so the recipe runner comes from here
378 # too rather than the host — `nix develop` and then `just run` is
379 # the whole of what a machine with nix needs.
380 packages = [ jolt pkgs.babashka pkgs.just (nixGLFor pkgs) ];
Run this tree on a native half the builders made a32699e nandi 17d ago381
382 # Read by scripts/run.bb rather than baked into a wrapper: the frq
383 # source `just run` runs is the working tree, so the launcher has
384 # to be a script in that tree and the shell has to hand it its
385 # answers. Naming these is also what makes the shell build them.
386 JOLT_NATIVE_LIB = "${native}/lib";
387 GLIMMER_SRC = glimmer;
388 GLIMMER_VIDYA_SRC = "${jolt-native}/jolt/glimmer-vidya";
389 FRQ_LIB_PATH = lib.makeLibraryPath (runtimeLibsFor pkgs);
390 NIXGL = "${nixGLFor pkgs}/bin/nixGLIntel";
391 };
392 });
393
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago394 apps = forEachSystem (pkgs: {
395 default = {
396 type = "app";
397 program = "${self.packages.${pkgs.stdenv.hostPlatform.system}.frq}/bin/frq";
398 };
399 });
400 };
401}