nandi/frqpublic Fork 0
4f04b910c974db5746fb6143b2f89abfa07d827d
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 · 394 lines · 16.4 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
Pin the native half to the release the rest of the tree names cb7f9f1 nandi 17d ago32 # v0.1.3, which is the rev deps.edn and the scripts/*.dotslash pins both
33 # name. Pinned, and pinned to that: this input carries both halves of
34 # glimmer-vidya — libvidya, and the Jolt side that binds it — so an
35 # unpinned `main` is a build whose native half is free to sit at a
36 # different commit from the tree that talks to it. It did, and what the
37 # drift cost was silence: the Jolt half sent a reaction pill's hover card
38 # 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 ago39 jolt-native = {
Pin the native half to the release the rest of the tree names cb7f9f1 nandi 17d ago40 url = "git+https://gitlab.com/nandithebull/jolt-native?rev=fd0e21a6c5d745ff9d134f92f909665454a7a1c9";
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago41 flake = false;
42 };
43
Paint the same screens into a terminal ab83b42 nandi 17d ago44 # The terminal backend, which v0.1.3 has not got: crates/jolt-tui (the tree
45 # ABI over a grid of cells) and jolt/glimmer-tui (the jolt side that binds
46 # it). Its own input rather than a bump of the one above, deliberately —
47 # the window half stays pinned to the release the rest of the tree names,
48 # and only `tui` evaluates this. When the backend ships in a release the
49 # two become one pin again.
50 jolt-native-tui = {
51 url = "git+https://gitlab.com/nandithebull/jolt-native?rev=3cfee15a9d938584f526f606f853771eaad7f56c";
52 flake = false;
53 };
54
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago55 # Chez itself, because the APK needs a cross target nixpkgs does not
56 # build: frq's Scheme is compiled to an arm64 boot image, and that wants
57 # Chez's own `tarm64le` workarea — boot files, xpatch and libkernel.a.
58 # The version is the one the hand-built tree under ~/.cache used, and the
59 # submodules are not optional (zuo builds it, lz4 and zlib link into it).
60 # The fork jolt's own Android pin names, built here rather than fetched as
61 # a release binary: upstream reads the socket address out of `struct
62 # addrinfo` at glibc's offset, which on Bionic is `ai_canonname`, so an APK
63 # built with upstream cannot open a TLS connection at all. Only the boot
64 # image uses it; the desktop package still builds jolt-src.
65 jolt-android-src = {
66 url = "git+https://gitlab.com/nandithebull/jolt?rev=2b80d68d1f7a31ba92b208b3957e5fb555617ada&submodules=1";
67 flake = false;
68 };
69
70 chez-src = {
71 url = "git+https://github.com/cisco/ChezScheme?ref=refs/tags/v10.4.1&submodules=1";
72 flake = false;
73 };
74
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago75 # The sha deps.edn pins, on the fork with the reconciler fixes.
76 glimmer = {
77 url = "git+https://gitlab.com/nandithebull/glimmer?rev=399df371c790d690fb6e4560c3d4d7f838502857";
78 flake = false;
79 };
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago80
81 # Only ever used off NixOS, to put the host GL driver on the loader path.
82 nixgl = {
83 url = "github:nix-community/nixGL";
84 inputs.nixpkgs.follows = "nixpkgs";
85 };
Bundle the closure into one file the AppImage runtime mounts f82b93f nandi 18d ago86
87 # Wraps a closure into a single self-extracting file. Only the `appimage`
88 # output evaluates it.
89 nix-appimage = {
90 url = "github:ralismark/nix-appimage";
91 inputs.nixpkgs.follows = "nixpkgs";
92 };
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago93 };
94
Paint the same screens into a terminal ab83b42 nandi 17d ago95 outputs = { self, nixpkgs, jolt-src, jolt-native, jolt-native-tui, glimmer, chez-src, jolt-android-src, nixgl, nix-appimage }:
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago96 let
97 systems = [ "x86_64-linux" "aarch64-linux" ];
98 forEachSystem = f:
99 nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
100 in
101 {
102 packages = forEachSystem (pkgs:
103 let
104 inherit (pkgs) lib;
105
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago106 # Mesa, despite the name: it covers Intel and AMD alike. The NVIDIA
107 # wrappers are the ones that need --impure (they read the host kernel
108 # module's version), which is why this only ever reaches for Intel.
109 nixGL = nixgl.packages.${pkgs.stdenv.hostPlatform.system}.nixGLIntel;
110
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago111 # libvidya (the retained-tree ABI glimmer-vidya binds, on egui) and
112 # libjoltmoq (the AV media plane). One workspace, two cdylibs.
113 native = pkgs.rustPlatform.buildRustPackage {
114 pname = "jolt-native";
115 version = "0.1.0";
116 src = jolt-native;
117
118 cargoLock = {
119 lockFile = "${jolt-native}/Cargo.lock";
120 allowBuiltinFetchGit = true;
121 };
122
123 nativeBuildInputs = with pkgs; [
124 pkg-config
125 cmake
126 rustPlatform.bindgenHook
127 ];
128
129 buildInputs = with pkgs; [
130 alsa-lib
131 pipewire
132 openssl
133 libxkbcommon
134 wayland
135 libGL
136 ];
137
138 # Upstream's .cargo/config.toml drives the whole build through
139 # DotSlash: a pinned rustc, sccache, and zig as the C/C++ compiler
140 # and linker, each fetched from the network on first use. None of
141 # that survives a build sandbox, and none of it is needed when the
142 # toolchain comes from the store — so drop it and let stdenv's cc
143 # link (openh264 is C++, which stdenv covers too).
144 postPatch = ''
145 rm -f .cargo/config.toml
146 '';
147
148 # bindgen reads linux/videodev2.h directly; upstream points it at
149 # zig's bundled headers, which under Nix is just the kernel headers.
150 V4L2R_VIDEODEV2_H_PATH = "${pkgs.linuxHeaders}/include";
151
152 doCheck = false;
153
154 installPhase = ''
155 runHook preInstall
156 mkdir -p "$out/lib"
157 install -m644 target/*/release/*.so "$out/lib/"
158 runHook postInstall
159 '';
160 };
161
Paint the same screens into a terminal ab83b42 nandi 17d ago162 # libjolttui alone, out of the tui input's workspace. One crate
163 # rather than the whole of it: the terminal backend's dependencies
164 # are crossterm and a width table, where libvidya's and libjoltmoq's
165 # are egui, openh264 and v4l — none of which a terminal needs, and
166 # all of which this would otherwise build a second time at a second
167 # rev.
168 nativeTui = pkgs.rustPlatform.buildRustPackage {
169 pname = "jolt-tui";
170 version = "0.1.0";
171 src = jolt-native-tui;
172
173 cargoLock = {
174 lockFile = "${jolt-native-tui}/Cargo.lock";
175 allowBuiltinFetchGit = true;
176 };
177
178 nativeBuildInputs = with pkgs; [ pkg-config cmake rustPlatform.bindgenHook ];
179 buildInputs = with pkgs; [ alsa-lib pipewire openssl libxkbcommon wayland libGL ];
180
181 cargoBuildFlags = [ "-p" "jolt-tui" ];
182
183 # As above: upstream's .cargo/config.toml drives the build through
184 # DotSlash, which a sandbox has no network for.
185 postPatch = ''
186 rm -f .cargo/config.toml
187 '';
188
189 V4L2R_VIDEODEV2_H_PATH = "${pkgs.linuxHeaders}/include";
190 doCheck = false;
191
192 installPhase = ''
193 runHook preInstall
194 mkdir -p "$out/lib"
195 install -m644 target/*/release/libjolttui.so "$out/lib/"
196 runHook postInstall
197 '';
198 };
199
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago200 # 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 ago201 # A function, because there are two of them — upstream for the
202 # desktop, and the Bionic-addrinfo fork for the boot image the APK
203 # carries. Nothing else about the build differs.
204 joltFrom = src: pkgs.stdenv.mkDerivation {
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago205 pname = "jolt";
206 version = "dev";
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago207 inherit src;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago208
209 strictDeps = true;
210 nativeBuildInputs = with pkgs; [ chez makeWrapper pkg-config xxd ];
211 buildInputs = with pkgs; [ lz4 zlib ncurses openssl libuuid ];
212
213 JOLT_VERSION = "dev";
214 dontConfigure = true;
215
216 buildPhase = ''
217 runHook preBuild
218 scheme --script host/chez/build-jolt.ss release target/release/jolt
219 runHook postBuild
220 '';
221
222 installPhase = ''
223 runHook preInstall
224 mkdir -p "$out/bin"
225 install -m755 target/release/jolt "$out/bin/jolt"
226 runHook postInstall
227 '';
228
229 # jolt.deps shells out to git and unzip, and jolt.mvn-http dlopens
230 # OpenSSL through the JOLT_OPENSSL_LIBDIR seam.
Read the clock in the reader's zone when /etc/localtime is a file bffe879 nandi 18d ago231 #
232 # TZDIR so a zone *name* resolves wherever this runs: frq.clock
233 # hands one to tzset, and glibc then looks for the tzfile under
234 # /usr/share/zoneinfo unless told otherwise — which a NixOS host
235 # does not have. The store's own tzdata is there on both kinds of
236 # 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 ago237 postFixup = ''
238 wrapProgram "$out/bin/jolt" \
239 --prefix PATH : "${pkgs.lib.makeBinPath [ pkgs.git pkgs.unzip ]}" \
240 --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 ago241 --set-default TZDIR "${pkgs.tzdata}/share/zoneinfo" \
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago242 --set-default SSL_CERT_FILE "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
243 '';
244 };
245
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago246 joltRuntime = joltFrom jolt-src;
247 joltAndroid = joltFrom jolt-android-src;
248
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago249 # glimmer-vidya lives inside the jolt-native checkout, and its own
250 # deps.edn asks for glimmer by git — the top-level override below
251 # answers for both.
252 glimmerVidya = "${jolt-native}/jolt/glimmer-vidya";
Paint the same screens into a terminal ab83b42 nandi 17d ago253 glimmerTui = "${jolt-native-tui}/jolt/glimmer-tui";
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago254
255 # egui reaches for these with dlopen rather than linking them, so
256 # being in the cdylib's buildInputs is not enough — the launcher has
257 # to put them on the loader path itself. Without libX11 here, vidya
258 # reports "X11 unavailable", falls back to Wayland, and winit refuses
259 # to build a second event loop after the failed first one.
260 runtimeLibs = with pkgs; [
261 libGL
262 libxkbcommon
263 wayland
264 xorg.libX11
265 xorg.libXcursor
266 xorg.libXi
267 xorg.libXrandr
268 vulkan-loader
269 ];
270
271 # The project as jolt sees it: source, deps.edn, nothing else.
272 frqSource = pkgs.runCommand "frq-source" { } ''
273 mkdir -p "$out"
274 cp -r ${self}/src ${self}/deps.edn "$out/"
275 '';
276
277 # Jolt resolves deps.edn from the working directory, so the launcher
278 # runs from the store copy. Its .jolt/cpcache write lands on a
279 # read-only directory and jolt treats that as a quiet cache miss, so
280 # the only cost is re-resolving the (already local) graph per start.
281 frqScript = pkgs.writeShellScript "frq" ''
282 export LD_LIBRARY_PATH="${native}/lib:${lib.makeLibraryPath runtimeLibs}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
283 cd ${frqSource}
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago284
285 # On NixOS the store's Mesa is the system's and the window opens.
286 # Anywhere else the real driver is the host's, so defer to nixGL
287 # it prepends the host driver, which has to win over ours.
288 runner=""
289 [ -e /run/current-system ] || runner="${nixGL}/bin/nixGLIntel"
290
291 exec ''${runner} ${joltRuntime}/bin/jolt \
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago292 -Sdeps '{:deps {jolt-lang/glimmer {:local/root "${glimmer}"} nandi/glimmer-vidya {:local/root "${glimmerVidya}"}}}' \
293 -M:frq "$@"
294 '';
295
Paint the same screens into a terminal ab83b42 nandi 17d ago296 # The same source, the other backend. No GL, no nixGL and no X11 —
297 # a terminal is the one surface that needs nothing from the host but
298 # a terminal, which is the reason this output exists.
299 tuiScript = pkgs.writeShellScript "frq-tui" ''
300 export LD_LIBRARY_PATH="${nativeTui}/lib:${native}/lib''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
301 cd ${frqSource}
302
303 exec ${joltRuntime}/bin/jolt \
304 -Sdeps '{:deps {jolt-lang/glimmer {:local/root "${glimmer}"} nandi/glimmer-vidya {:local/root "${glimmerVidya}"} nandi/glimmer-tui {:local/root "${glimmerTui}"}}}' \
305 -m frq.tui "$@"
306 '';
307
308 tui = pkgs.runCommand "frq-tui-0.1.0"
309 {
310 meta = {
311 description = "frq's screens in a terminal";
312 mainProgram = "frq-tui";
313 platforms = systems;
314 };
315 }
316 ''
317 mkdir -p "$out/bin"
318 ln -s ${tuiScript} "$out/bin/frq-tui"
319 '';
320
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago321 frq = pkgs.runCommand "frq-0.1.0"
322 {
323 meta = {
324 description = "A freeq client in jolt";
325 mainProgram = "frq";
326 platforms = systems;
327 };
328 }
329 ''
330 mkdir -p "$out/bin"
331 ln -s ${frqScript} "$out/bin/frq"
332 '';
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago333 # --- Android ------------------------------------------------------
334 # The SDK and the NDK are Google's, which means unfree and a licence
335 # to accept — so this is its own import of nixpkgs rather than the
336 # `legacyPackages` everything above uses. Confined to the Android
337 # outputs: `nix build` of frq itself never evaluates it.
338 #
339 # The NDK here is r29, which is the version scripts/android-ndk.dotslash
340 # pins and the one the pinned libvidya was built with.
341 androidPkgs = import nixpkgs {
342 inherit (pkgs.stdenv.hostPlatform) system;
343 config = {
344 allowUnfree = true;
345 android_sdk.accept_license = true;
346 };
347 };
348
349 androidComposition = androidPkgs.androidenv.composeAndroidPackages {
350 buildToolsVersions = [ "36.0.0" ];
351 platformVersions = [ "36" ];
352 includeNDK = true;
353 };
354
355 android = import ./nix/android.nix {
356 inherit pkgs self chez-src jolt-native glimmer joltAndroid;
357 inherit (pkgs) lib;
358 androidSdk = androidComposition.androidsdk;
359 ndk = androidComposition.ndk-bundle;
360 };
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago361 in
362 {
363 inherit native frq;
Paint the same screens into a terminal ab83b42 nandi 17d ago364 inherit nativeTui tui;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago365 jolt = joltRuntime;
366 default = frq;
Bundle the closure into one file the AppImage runtime mounts f82b93f nandi 18d ago367
368 # frq and everything it loads, squashed into one runnable file for
369 # hosts without Nix. The whole closure rides along — Mesa included,
370 # which is not waste: off NixOS the launcher goes through nixGL, and
371 # nixGL needs a store Mesa to put the host's driver in front of.
372 appimage =
373 nix-appimage.bundlers.${pkgs.stdenv.hostPlatform.system}.default frq;
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago374 }
375 # An APK is built by a linux-x86_64 NDK and a linux-x86_64 jolt, and
376 # Google ships no other; on aarch64 the Android outputs are simply
377 # absent rather than present and broken.
378 // lib.optionalAttrs (pkgs.stdenv.hostPlatform.system == "x86_64-linux") {
379 inherit (android) apk chezAndroid joltBoot libjoltapp;
380 apk-unsigned = android.apk-unsigned;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago381 });
382
383 apps = forEachSystem (pkgs: {
384 default = {
385 type = "app";
386 program = "${self.packages.${pkgs.stdenv.hostPlatform.system}.frq}/bin/frq";
387 };
Paint the same screens into a terminal ab83b42 nandi 17d ago388 tui = {
389 type = "app";
390 program = "${self.packages.${pkgs.stdenv.hostPlatform.system}.tui}/bin/frq-tui";
391 };
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago392 });
393 };
394}