nandi/frqpublic Fork 0
1f55e68cf7056290fe48cbd817e396a8d22df1e2
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 · 555 lines · 27.3 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";
Take jolt and jolt-native as flakes, off my own fork aaa560d nandi 13d ago24 # `git+https` with `?submodules=1` rather than the github scheme: Jolt's
25 # own flake declares `self.submodules`, which this Nix rejects when the
26 # flake is fetched as `github:`. Its outputs are not what we take — the
27 # runtime is built here, by joltFrom — but it is a flake all the same, so
28 # its own inputs are locked with ours rather than left to float, and
29 # `vendor/` comes along as the submodule the build needs.
30 #
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago31 # The fork rather than jolt-lang/jolt, and unpinned: the desktop follows
32 # the fork's main. It used to be paired with a second, pinned input for
33 # the APK's boot image; there is no jolt APK now, so there is one runtime
34 # and one rev.
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago35 jolt-src = {
Take jolt and jolt-native as flakes, off my own fork aaa560d nandi 13d ago36 url = "git+https://gitlab.com/nandithebull/jolt?submodules=1";
37 inputs.nixpkgs.follows = "nixpkgs";
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago38 };
39
Take the Android objects from jolt-native's CI, always the latest 472e179 nandi 11d ago40 # The source half of jolt-native: the Jolt code under glimmer-backends/ that
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago41 # binds the native objects, and the flake that builds them. This input is
42 # what `just run` builds against.
Take the jolt-native ahead of the release, since a run can build it 36f6c77 nandi 17d ago43 #
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago44 # It carries both backends that are left — glimmer-cosmic over
45 # libjoltcosmic for the window, glimmer-tui over libjolttui for the
46 # terminal — and no longer jvui or vidya, which were experiments.
Stop building the media plane frq no longer loads 231b9d3 nandi 9d ago47 #
Pin jolt-native at the merge rather than at the branch 3d7c428 nandi 9d ago48 # Pinned all the same, and pinned to a rev, because an
Pin the native half to the release the rest of the tree names cb7f9f1 nandi 17d ago49 # unpinned `main` is a build whose native half is free to sit at a
50 # different commit from the tree that talks to it. It did, and what the
51 # drift cost was silence: the Jolt half sent a reaction pill's hover card
52 # to a libvidya with no handler for one, and the pill said nothing.
Merge origin/main, and let one jolt-native answer for both backends 1e8b590 nandi 16d ago53 # It also carries the terminal backend — crates/jolt-tui, the same tree ABI
54 # over a grid of cells, and jolt/glimmer-tui beside glimmer-vidya. That was
55 # a second input at a second rev while it lived on a branch, which is the
56 # drift this comment warns about wearing a different hat: one input, and
57 # the window and the terminal are the same library either way.
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago58 jolt-native = {
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago59 url = "git+https://gitlab.com/nandithebull/jolt-native?rev=4706c920e45ce80b11ee106d05c16d9eacc99fc7";
Take jolt and jolt-native as flakes, off my own fork aaa560d nandi 13d ago60 inputs.nixpkgs.follows = "nixpkgs";
Paint the same screens into a terminal ab83b42 nandi 17d ago61 };
62
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago63 # The sha deps.edn pins, on the fork with the reconciler fixes.
64 glimmer = {
65 url = "git+https://gitlab.com/nandithebull/glimmer?rev=399df371c790d690fb6e4560c3d4d7f838502857";
66 flake = false;
67 };
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago68
69 # Only ever used off NixOS, to put the host GL driver on the loader path.
70 nixgl = {
71 url = "github:nix-community/nixGL";
72 inputs.nixpkgs.follows = "nixpkgs";
73 };
Bundle the closure into one file the AppImage runtime mounts f82b93f nandi 18d ago74
75 # Wraps a closure into a single self-extracting file. Only the `appimage`
76 # output evaluates it.
77 nix-appimage = {
78 url = "github:ralismark/nix-appimage";
79 inputs.nixpkgs.follows = "nixpkgs";
80 };
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago81 };
82
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago83 outputs = { self, nixpkgs, jolt-src, jolt-native, glimmer, nixgl, nix-appimage }:
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago84 let
85 systems = [ "x86_64-linux" "aarch64-linux" ];
86 forEachSystem = f:
87 nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
Run this tree on a native half the builders made a32699e nandi 17d ago88
89 # Mesa, despite the name: it covers Intel and AMD alike. The NVIDIA
90 # wrappers are the ones that need --impure (they read the host kernel
91 # 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 ago92 #
93 # Built from nixGL's default.nix rather than taken from its flake
94 # outputs, for the one argument the flake hardcodes on: `enable32bits`,
95 # which on x86_64 puts a second, i686 copy of mesa, its LLVM, and
96 # intel-media-driver into the wrapper. frq is 64-bit on both halves —
97 # the Rust cdylibs and the Chez runtime — so nothing here ever opens the
98 # 32-bit driver, and carrying it is most of the dev shell's closure.
99 nixGLFor = pkgs: (import nixgl {
100 inherit pkgs;
101 enable32bits = false;
102 }).nixGLIntel;
Run this tree on a native half the builders made a32699e nandi 17d ago103
104 # egui reaches for these with dlopen rather than linking them, so being
105 # 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 ago106 # 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 ago107 # "X11 unavailable", falls back to Wayland, and winit refuses to build a
108 # second event loop after the failed first one.
109 #
110 # Out here rather than beside the package that first needed them: the
111 # dev shell starts frq too, on this tree's source rather than the store's
112 # copy of it, and a second copy of this list is a second chance for the
113 # two ways of running to disagree about what the window needs.
114 runtimeLibsFor = pkgs: with pkgs; [
115 libGL
116 libxkbcommon
117 wayland
Follow the xorg renames, and stop wrapping nix in distrobox a400a00 nandi 16d ago118 libx11
119 libxcursor
120 libxi
121 libxrandr
Run this tree on a native half the builders made a32699e nandi 17d ago122 vulkan-loader
123 ];
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago124 in
125 {
126 packages = forEachSystem (pkgs:
127 let
128 inherit (pkgs) lib;
129
Run this tree on a native half the builders made a32699e nandi 17d ago130 nixGL = nixGLFor pkgs;
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago131
Take the native objects from their own flake rather than rebuilding them 0ee8bba nandi 13d ago132 # libvidya (the retained-tree ABI glimmer-vidya binds, on egui),
133 # libjolttui (the same tree over a grid of cells) and libjoltmoq (the
134 # AV media plane) — one workspace, three cdylibs, taken from
135 # jolt-native's own flake rather than rebuilt here.
136 #
137 # This used to be a rustPlatform.buildRustPackage over the same
138 # source, which meant restating upstream's build: the seven git deps
139 # hashed by hand in `cargoLock.outputHashes` and re-hashed whenever
140 # its Cargo.lock moved, the linuxHeaders path v4l2r's bindgen wants,
141 # and a postPatch dropping the .cargo/config.toml that pointed the
142 # build at DotSlash. Upstream's flake says all of that itself now,
143 # and says it once. It also builds cpal with the `pipewire` feature,
144 # which the restatement did not — so device names in a call are
145 # PipeWire's rather than raw ALSA PCMs.
Paint frq with jvui, and take the last Rust out of the window 77963dc nandi 9d ago146 # libjolttui only. Not libjoltmoq, whose job `frq.av.plane` does
147 # now, and no longer libvidya either: the window is jvui on SDL,
148 # so the only object left out of that Cargo workspace is the
149 # terminal backend, and only `just tui` loads it.
Switch frq.av onto the jolt media plane, and stop shipping libjoltmoq b33f288 nandi 9d ago150 #
151 # This makes the closure smaller and the APK smaller. It does NOT
152 # make the build shorter, and it is worth being exact about why:
153 # jolt-native compiles its external crates ONCE, in a
154 # `buildDepsOnly` derivation shared by all three objects, so
155 # asking for two of them still builds every dependency the third
156 # has — the 440 crates that are jolt-moq's alone. Getting those
157 # out of the build is a change in jolt-native, not here: either
158 # jolt-moq leaves that workspace, or its deps artifact stops
159 # being workspace-wide.
160 native =
161 let np = jolt-native.packages.${pkgs.stdenv.hostPlatform.system};
162 in pkgs.symlinkJoin {
163 name = "jolt-native-ui";
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago164 # Both backends there are. libjoltcosmic is the window —
165 # libcosmic behind the same retained-tree ABI — and libjolttui
166 # is the terminal. Neither is libvidya and neither is jvui:
167 # those were experiments and are gone from this tree entirely.
168 paths = [ np.libjolttui np.libjoltcosmic ];
Switch frq.av onto the jolt media plane, and stop shipping libjoltmoq b33f288 nandi 9d ago169 };
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago170
Fetch libmoq_ffi again, and keep the codecs out of Rust f518938 nandi 9d ago171 # libmoq_ffi — MoQ over QUIC behind UniFFI's C ABI, FETCHED rather
172 # than built. This is the object `frq.moq.raw` is generated from.
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago173 #
Fetch libmoq_ffi again, and keep the codecs out of Rust f518938 nandi 9d ago174 # Fetched because building it is the thing this whole exercise is
175 # about: moq-ffi pulls moq-native, iroh, quinn, rustls and aws-lc-sys
176 # behind it, which is 440 crates that nothing else in this tree
177 # needs. Upstream already publishes the object for both Linux
178 # architectures, so we take those bytes.
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago179 #
Fetch libmoq_ffi again, and keep the codecs out of Rust f518938 nandi 9d ago180 # Pinned to a release and to a hash, and the hashes below are
181 # upstream's own published .sha256 files rather than ones observed
182 # here — a `nix-prefetch` of whatever the URL serves today would
183 # record that it downloaded something, not that it downloaded the
184 # right thing.
185 #
186 # WHAT THIS BUILD IS NOT: moq-ffi's `audio` and `video` features are
187 # on by default upstream and are OFF in these artifacts, so there is
188 # no publish_audio/publish_video and no moqaudio*/moqvideo* here —
189 # 206 functions where the Apple artifact has 230. That is why the
190 # bindings are generated from the object (`just gen-moq`) and not
191 # from the C header the release ships, which describes the Apple one.
192 moqFfi =
193 let
194 version = "0.3.17";
195 target = {
196 "x86_64-linux" = "x86_64-unknown-linux-gnu";
197 "aarch64-linux" = "aarch64-unknown-linux-gnu";
198 }.${pkgs.stdenv.hostPlatform.system};
199 hash = {
200 "x86_64-linux" = "sha256-dzQXpV4JgdtD+g33WX51FFAQdfCUXkNsx1xPbobPfUI=";
201 "aarch64-linux" = "sha256-PdzRwbJFqOZWRgI0HHX2XUH+Ljh4V3jvQ9asfvCuIPA=";
202 }.${pkgs.stdenv.hostPlatform.system};
203 in
204 pkgs.stdenv.mkDerivation {
205 pname = "libmoq-ffi";
206 inherit version;
207 src = pkgs.fetchurl {
208 url = "https://github.com/kixelated/moq/releases/download/moq-ffi-v${version}/moq-ffi-${version}-${target}-libmoq_ffi.so";
209 inherit hash;
210 };
211 dontUnpack = true;
212 # It carries no RUNPATH and needs libgcc_s, libm and libc — the
213 # host's on an ordinary distro, and nothing at all on NixOS
214 # unless they are bound here.
215 nativeBuildInputs = [ pkgs.autoPatchelfHook ];
216 buildInputs = [ pkgs.stdenv.cc.cc.lib ];
217 installPhase = ''
218 mkdir -p $out/lib
219 cp $src $out/lib/libmoq_ffi.so
220 chmod +w $out/lib/libmoq_ffi.so
221 '';
222 };
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago223
224 # One directory for the loader to look in. jolt resolves every
225 # :jolt/native name against JOLT_NATIVE_LIB, and the objects now come
226 # from two places — jolt-native's flake, and the moq-ffi release — so
227 # they are joined rather than the path being made a list, which the
228 # loader does not take.
Bind the C libraries a call actually needs 87e5be9 nandi 9d ago229 # The C codecs, from nixpkgs. libmoq_ffi carries the transport and
230 # nothing else — moq-ffi's `audio` and `video` features would have
231 # brought Opus and H.264 with them, at the price of compiling a
232 # 1062-crate workspace — so the codecs are linked here instead,
233 # where they have always lived.
234 #
235 # Named in :jolt/native, so the loader resolves them the same way it
236 # resolves libvidya: by name, out of one directory.
237 # A flat C face for openh264, because openh264 has none. Its
238 # `ISVCEncoder` is `const ISVCEncoderVtbl*` — every method is a
239 # function pointer in a vtable — and jolt.ffi cannot call one: Chez
240 # fixes a foreign procedure's types when it compiles it, and the
241 # target must be a literal C symbol name. So the vtable is walked in
242 # c/frq_h264.c and jolt binds the five plain symbols it exports.
243 #
244 # One translation unit against a library nixpkgs already has. It is
245 # a calling convention adapter, not a second media plane, and the
246 # distinction from the moq-ffi build it replaces is the whole point:
247 # this compiles one .c file, not a 1062-crate workspace.
248 frqH264 = pkgs.stdenv.mkDerivation {
249 pname = "frq-h264";
250 version = "0.1";
251 src = ./c;
252 nativeBuildInputs = [ pkgs.pkg-config ];
253 buildInputs = [ pkgs.openh264 ];
254 buildPhase = ''
255 $CC -O2 -fPIC -shared frq_h264.c -o libfrqh264.so \
256 $(pkg-config --cflags --libs openh264)
257 '';
258 installPhase = ''
259 mkdir -p $out/lib && cp libfrqh264.so $out/lib/
260 '';
261 };
262
263 # openh264 is here for frqH264's DT_NEEDED; alsa-lib for capture
264 # and playback. V4L2 needs nothing: it is ioctls against libc and
265 # the kernel, so there is no library to name.
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago266 # No SDL any more: it was jvui's, declared in jvui's own
267 # :jolt/native and dlopened by soname. libcosmic paints through wgpu
268 # and takes what it needs from `runtimeLibs` instead.
269 codecs = [ pkgs.libopus pkgs.openh264 frqH264 pkgs.alsa-lib ];
Bind the C libraries a call actually needs 87e5be9 nandi 9d ago270
Let the plane take real devices, not only test thunks 596dc29 nandi 9d ago271 # ALSA's PipeWire plugin, which is how `default` resolves to
272 # anything on a machine running PipeWire — and every machine frq
273 # targets does. Without it alsa-lib fails to dlopen
274 # libasound_module_pcm_pipewire.so and the only devices that open
275 # are raw hardware ones, which PipeWire is already holding.
276 #
277 # An environment variable rather than a library in the join:
278 # alsa-lib looks plugins up by directory, not by soname.
279 alsaPluginDir = "${pkgs.pipewire}/lib/alsa-lib";
280
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago281 nativeAll = pkgs.symlinkJoin {
282 name = "frq-native";
Bind the C libraries a call actually needs 87e5be9 nandi 9d ago283 paths = [ native moqFfi ] ++ codecs;
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago284 };
285
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago286 # Jolt itself: Clojure on Chez, built the way its own flake builds it.
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago287 #
288 # Still a function taking its source, though there is only one of
289 # them now: the second was the Bionic-addrinfo fork the APK's boot
290 # image carried, and there is no jolt APK any more — the phone is
291 # ClojureDart and Flutter, and jolt does not run there at all.
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago292 joltFrom = src: pkgs.stdenv.mkDerivation {
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago293 pname = "jolt";
294 version = "dev";
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago295 inherit src;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago296
297 strictDeps = true;
298 nativeBuildInputs = with pkgs; [ chez makeWrapper pkg-config xxd ];
299 buildInputs = with pkgs; [ lz4 zlib ncurses openssl libuuid ];
300
301 JOLT_VERSION = "dev";
302 dontConfigure = true;
303
304 buildPhase = ''
305 runHook preBuild
306 scheme --script host/chez/build-jolt.ss release target/release/jolt
307 runHook postBuild
308 '';
309
310 installPhase = ''
311 runHook preInstall
312 mkdir -p "$out/bin"
313 install -m755 target/release/jolt "$out/bin/jolt"
314 runHook postInstall
315 '';
316
317 # 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 ago318 # OpenSSL through the JOLT_OPENSSL_LIBDIR seam. gitMinimal rather
319 # than git: all jolt.deps asks for is clone/fetch/rev-parse, and
320 # the full package carries Perl and Python for the subcommands
321 # 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 ago322 #
323 # TZDIR so a zone *name* resolves wherever this runs: frq.clock
324 # hands one to tzset, and glibc then looks for the tzfile under
325 # /usr/share/zoneinfo unless told otherwise — which a NixOS host
326 # does not have. The store's own tzdata is there on both kinds of
327 # 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 ago328 postFixup = ''
329 wrapProgram "$out/bin/jolt" \
Stop carrying the 32-bit GL stack and half of git 040f2c1 nandi 16d ago330 --prefix PATH : "${pkgs.lib.makeBinPath [ pkgs.gitMinimal pkgs.unzip ]}" \
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago331 --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 ago332 --set-default TZDIR "${pkgs.tzdata}/share/zoneinfo" \
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago333 --set-default SSL_CERT_FILE "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
334 '';
335 };
336
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago337 joltRuntime = joltFrom jolt-src;
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago338
339 # The backends' Clojure halves, which live inside the jolt-native
340 # checkout beside the objects they bind. Their own deps.edn asks for
341 # glimmer by git — the top-level override below answers for both.
342 glimmerCosmic = "${jolt-native}/glimmer-backends/glimmer-cosmic";
Take the Android objects from jolt-native's CI, always the latest 472e179 nandi 11d ago343 glimmerTui = "${jolt-native}/glimmer-backends/glimmer-tui";
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago344
Run this tree on a native half the builders made a32699e nandi 17d ago345 runtimeLibs = runtimeLibsFor pkgs;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago346
347 # The project as jolt sees it: source, deps.edn, nothing else.
348 frqSource = pkgs.runCommand "frq-source" { } ''
349 mkdir -p "$out"
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago350 cp -r ${self}/common ${self}/src ${self}/deps.edn "$out/"
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago351 '';
352
353 # Jolt resolves deps.edn from the working directory, so the launcher
354 # runs from the store copy. Its .jolt/cpcache write lands on a
355 # read-only directory and jolt treats that as a quiet cache miss, so
356 # the only cost is re-resolving the (already local) graph per start.
357 frqScript = pkgs.writeShellScript "frq" ''
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago358 export LD_LIBRARY_PATH="${nativeAll}/lib:${lib.makeLibraryPath runtimeLibs}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
Let the plane take real devices, not only test thunks 596dc29 nandi 9d ago359 export ALSA_PLUGIN_DIR="${alsaPluginDir}"
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago360 cd ${frqSource}
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago361
362 # On NixOS the store's Mesa is the system's and the window opens.
363 # Anywhere else the real driver is the host's, so defer to nixGL
364 # it prepends the host driver, which has to win over ours.
365 runner=""
366 [ -e /run/current-system ] || runner="${nixGL}/bin/nixGLIntel"
367
368 exec ''${runner} ${joltRuntime}/bin/jolt \
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago369 -Sdeps '{:deps {jolt-lang/glimmer {:local/root "${glimmer}"} nandi/glimmer-cosmic {:local/root "${glimmerCosmic}"}}}' \
370 -m frq.cosmic "$@"
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago371 '';
372
Paint the same screens into a terminal ab83b42 nandi 17d ago373 # The same source, the other backend. No GL, no nixGL and no X11 —
374 # a terminal is the one surface that needs nothing from the host but
375 # a terminal, which is the reason this output exists.
376 tuiScript = pkgs.writeShellScript "frq-tui" ''
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago377 export LD_LIBRARY_PATH="${nativeAll}/lib''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
Let the plane take real devices, not only test thunks 596dc29 nandi 9d ago378 export ALSA_PLUGIN_DIR="${alsaPluginDir}"
Paint the same screens into a terminal ab83b42 nandi 17d ago379 cd ${frqSource}
380
381 exec ${joltRuntime}/bin/jolt \
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago382 -Sdeps '{:deps {jolt-lang/glimmer {:local/root "${glimmer}"} nandi/glimmer-tui {:local/root "${glimmerTui}"}}}' \
Paint the same screens into a terminal ab83b42 nandi 17d ago383 -m frq.tui "$@"
384 '';
385
386 tui = pkgs.runCommand "frq-tui-0.1.0"
387 {
388 meta = {
389 description = "frq's screens in a terminal";
390 mainProgram = "frq-tui";
391 platforms = systems;
392 };
393 }
394 ''
395 mkdir -p "$out/bin"
396 ln -s ${tuiScript} "$out/bin/frq-tui"
397 '';
398
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago399 frq = pkgs.runCommand "frq-0.1.0"
400 {
401 meta = {
402 description = "A freeq client in jolt";
403 mainProgram = "frq";
404 platforms = systems;
405 };
406 }
407 ''
408 mkdir -p "$out/bin"
409 ln -s ${frqScript} "$out/bin/frq"
410 '';
411 in
412 {
Bind the C libraries a call actually needs 87e5be9 nandi 9d ago413 inherit native moqFfi frqH264 nativeAll frq;
Let the plane take real devices, not only test thunks 596dc29 nandi 9d ago414 inherit (pkgs) pipewire;
Merge origin/main, and let one jolt-native answer for both backends 1e8b590 nandi 16d ago415 inherit tui;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago416 jolt = joltRuntime;
417 default = frq;
Bundle the closure into one file the AppImage runtime mounts f82b93f nandi 18d ago418
419 # frq and everything it loads, squashed into one runnable file for
420 # hosts without Nix. The whole closure rides along — Mesa included,
421 # which is not waste: off NixOS the launcher goes through nixGL, and
422 # nixGL needs a store Mesa to put the host's driver in front of.
423 appimage =
424 nix-appimage.bundlers.${pkgs.stdenv.hostPlatform.system}.default frq;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago425 });
426
Run this tree on a native half the builders made a32699e nandi 17d ago427 # Where `just run` runs, and — because entering it realises what it
428 # names — what builds the half of frq that is not this working tree.
429 #
430 # The two halves, and the split is the whole point of the shell. The frq
431 # source is the files on disk, uncommitted edits and all. Everything
432 # under it — jolt, glimmer, glimmer-vidya, both native objects — is the
433 # flake's, at the revs flake.lock names, so a run says what it ran
434 # against and both halves of glimmer-vidya move together. That is the
435 # drift the `jolt-native` input's comment is about, and a pin frq can
436 # answer for is worth more here than the convenience of a checkout.
437 #
Take the native objects from their own flake rather than rebuilding them 0ee8bba nandi 13d ago438 # `native` is jolt-native's own flake output. It was a buck2 graph when
439 # this comment was first written and a cargo build restated here when it
440 # was second: buck2 fetches its rustc, zig and every third-party crate as
441 # it goes and writes buck-out into the tree it builds, so a sandbox with
442 # no network and a read-only store was the one place it could not run.
443 # Upstream builds with nix now, so the thing its CI runs and the thing
444 # this shell hands a builder are the same derivation.
Run this tree on a native half the builders made a32699e nandi 17d ago445 #
446 # Nothing here says "nixbuild", though: it is a plain derivation, and
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago447 # where it gets built is the machine's business. The `run` recipe asks for
Run this tree on a native half the builders made a32699e nandi 17d ago448 # the shell with --max-jobs 0, which is what sends it to the `builders`
449 # entry rather than compiling egui on a laptop.
450 devShells = forEachSystem (pkgs:
451 let
452 inherit (pkgs) lib;
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago453 inherit (self.packages.${pkgs.stdenv.hostPlatform.system}) jolt native nativeAll;
Run this tree on a native half the builders made a32699e nandi 17d ago454 in
455 {
456 default = pkgs.mkShellNoCC {
457 name = "frq";
458
459 # jolt, because the runtime frq is run by should be the flake's
460 # too. nixGL for the same reason the launcher reaches for it — see
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago461 # frqScript. just so the recipe runner comes from here too rather
462 # than the host — `nix develop` and then `just run` is the whole of
463 # what a machine with nix needs.
464 packages = [ jolt pkgs.just (nixGLFor pkgs) ];
465
466 # Read by the recipes rather than baked into a wrapper: the frq
Run this tree on a native half the builders made a32699e nandi 17d ago467 # source `just run` runs is the working tree, so the launcher has
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago468 # to live in that tree and the shell has to hand it its answers.
469 # Naming these is also what makes the shell build them.
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago470 JOLT_NATIVE_LIB = "${nativeAll}/lib";
Let the plane take real devices, not only test thunks 596dc29 nandi 9d ago471 # Spelled out rather than shared with the packages block, which
472 # is a different `let`. See `alsaPluginDir` there for why.
473 ALSA_PLUGIN_DIR = "${pkgs.pipewire}/lib/alsa-lib";
Run this tree on a native half the builders made a32699e nandi 17d ago474 GLIMMER_SRC = glimmer;
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago475 GLIMMER_COSMIC_SRC = "${jolt-native}/glimmer-backends/glimmer-cosmic";
Take the Android objects from jolt-native's CI, always the latest 472e179 nandi 11d ago476 GLIMMER_TUI_SRC = "${jolt-native}/glimmer-backends/glimmer-tui";
Run this tree on a native half the builders made a32699e nandi 17d ago477 FRQ_LIB_PATH = lib.makeLibraryPath (runtimeLibsFor pkgs);
478 NIXGL = "${nixGLFor pkgs}/bin/nixGLIntel";
Take the jolt-native beside this one when there is one e10e1c1 nandi 9d ago479
480 # A checkout of jolt-native beside this one, in place of the pin.
481 #
482 # The pin is a rev on a server, so the loop for a change to the
483 # terminal backend was commit, push, re-pin, re-lock — four steps
484 # and an upload for a line of Rust. With a working copy beside this
485 # one the loop is `cargo build` and `just tui`, and the shell finds
486 # that copy itself: ../jolt-native from the checkout this was run
487 # in, which is where it is on the machines this is developed on.
488 # A worktree under .claude/worktrees counts as the same checkout —
489 # the sibling is the main one's, not the worktree's.
490 #
491 # Found rather than named, but not silently: it says which tree it
492 # took on the way in, because `just tui` running something other
493 # than the pin is the sort of thing you have to be able to see.
494 #
495 # It has to be a built one. A checkout with no target/release/
496 # libjolttui.so in it would mean the Jolt half of the backend from
497 # the working copy and the shared object from the pin — two halves
498 # of two different libraries, which fail in ways that look like
499 # neither. So an unbuilt sibling is left alone and the pin stands.
500 #
501 # FRQ_JOLT_NATIVE overrides the search, and is taken even unbuilt
502 # (with a word about what to run): naming a tree is asking for it.
503 # Empty is how you say the pin, on a machine that has a sibling and
504 # wants what everyone else is running.
505 #
506 # Only the sources and libjolttui move either way. Everything else
507 # on the library path — libopus, libmoq_ffi, the ALSA plugins —
508 # stays the pin's, since a checkout has no build of those to offer.
509 shellHook = ''
510 frq_named=1
511 if [ -z "''${FRQ_JOLT_NATIVE+named}" ]; then
512 frq_named=
513 frq_git="$(git rev-parse --path-format=absolute --git-common-dir 2>/dev/null || true)"
514 frq_near="''${frq_git:+$(dirname "$(dirname "$frq_git")")/jolt-native}"
515 if [ -n "$frq_near" ] && [ -e "$frq_near/target/release/libjolttui.so" ]; then
516 FRQ_JOLT_NATIVE="$frq_near"
517 else
518 FRQ_JOLT_NATIVE=""
519 fi
520 fi
521 if [ -n "$FRQ_JOLT_NATIVE" ]; then
522 if [ -d "$FRQ_JOLT_NATIVE/crates/jolt-tui" ]; then
523 FRQ_JOLT_NATIVE="$(cd "$FRQ_JOLT_NATIVE" && pwd)"
524 export FRQ_JOLT_NATIVE
525 export GLIMMER_TUI_SRC="$FRQ_JOLT_NATIVE/glimmer-backends/glimmer-tui"
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago526 export GLIMMER_COSMIC_SRC="$FRQ_JOLT_NATIVE/glimmer-backends/glimmer-cosmic"
Take the jolt-native beside this one when there is one e10e1c1 nandi 9d ago527 # First, so a cargo build wins over the pin's copy of the
528 # same object. The rest of the pin's lib directory is still
529 # behind it.
530 export JOLT_NATIVE_LIB="$FRQ_JOLT_NATIVE/target/release:$JOLT_NATIVE_LIB"
531 echo "frq: jolt-native from $FRQ_JOLT_NATIVE, not the pin (FRQ_JOLT_NATIVE= for the pin)" >&2
532 if [ ! -e "$FRQ_JOLT_NATIVE/target/release/libjolttui.so" ]; then
533 echo "frq: no libjolttui.so there yet cargo build --release --features terminal -p jolt-tui" >&2
534 fi
535 elif [ -n "$frq_named" ]; then
536 echo "frq: FRQ_JOLT_NATIVE=$FRQ_JOLT_NATIVE is not a jolt-native checkout; using the pin" >&2
537 fi
538 fi
539 unset frq_named frq_git frq_near
540 '';
Run this tree on a native half the builders made a32699e nandi 17d ago541 };
542 });
543
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago544 apps = forEachSystem (pkgs: {
545 default = {
546 type = "app";
547 program = "${self.packages.${pkgs.stdenv.hostPlatform.system}.frq}/bin/frq";
548 };
Paint the same screens into a terminal ab83b42 nandi 17d ago549 tui = {
550 type = "app";
551 program = "${self.packages.${pkgs.stdenv.hostPlatform.system}.tui}/bin/frq-tui";
552 };
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago553 });
554 };
555}