nandi/frqpublic Fork 0
231b9d33f1ae397628c90e2eb9d00aaa1b9fd405
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 · 563 lines · 27.2 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 #
31 # The fork rather than jolt-lang/jolt: it is what jolt-android-src already
32 # pins for the boot image, and a desktop runtime built from a different
33 # tree than the APK's is the same drift the jolt-native comment warns
34 # about. Unpinned here — the desktop follows the fork's main, while the
35 # APK stays on the rev below.
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago36 jolt-src = {
Take jolt and jolt-native as flakes, off my own fork aaa560d nandi 13d ago37 url = "git+https://gitlab.com/nandithebull/jolt?submodules=1";
38 inputs.nixpkgs.follows = "nixpkgs";
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago39 };
40
Take the Android objects from jolt-native's CI, always the latest 472e179 nandi 11d ago41 # The source half of jolt-native: the Jolt code under glimmer-backends/ that
42 # binds the native objects, and the flake that builds the desktop ones. The
43 # Android objects no longer come from here — jolt-native-android below
44 # fetches those prebuilt — so this input is what `just run` builds against
45 # and what an APK's Clojure side is read from, both at this rev.
Take the jolt-native ahead of the release, since a run can build it 36f6c77 nandi 17d ago46 #
47 # 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 ago48 # glimmer-vidya — libvidya, and the Jolt side that binds it — so an
Stop building the media plane frq no longer loads 231b9d3 nandi 9d ago49 # Pinned to `no-moq-deps`, which is the rev below plus one commit: it
50 # splits jolt-native's dependency artifact by consumer, so asking for
51 # libvidya and libjolttui no longer builds jolt-moq's 394 crates. It is
52 # branched from the rev this used to name rather than taken off main,
53 # because main has moved on to the Zig/dvui backend and a UI change is
54 # not what this pin is for.
55 #
Pin the native half to the release the rest of the tree names cb7f9f1 nandi 17d ago56 # unpinned `main` is a build whose native half is free to sit at a
57 # different commit from the tree that talks to it. It did, and what the
58 # drift cost was silence: the Jolt half sent a reaction pill's hover card
59 # 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 ago60 # It also carries the terminal backend — crates/jolt-tui, the same tree ABI
61 # over a grid of cells, and jolt/glimmer-tui beside glimmer-vidya. That was
62 # a second input at a second rev while it lived on a branch, which is the
63 # drift this comment warns about wearing a different hat: one input, and
64 # 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 ago65 jolt-native = {
Stop building the media plane frq no longer loads 231b9d3 nandi 9d ago66 url = "git+https://gitlab.com/nandithebull/jolt-native?rev=2975a4318cc748dd0ee25d6cef64f29c87bb1cf7";
Take jolt and jolt-native as flakes, off my own fork aaa560d nandi 13d ago67 inputs.nixpkgs.follows = "nixpkgs";
Paint the same screens into a terminal ab83b42 nandi 17d ago68 };
69
Take the Android objects from jolt-native's CI, always the latest 472e179 nandi 11d ago70 # The Android objects, prebuilt by jolt-native's CI rather than compiled
71 # here: an APK needs libvidya and libjoltmoq for arm64, and building them
72 # locally means an NDK, a Rust cross toolchain and the whole crane graph
73 # for two files that upstream already built and published.
74 #
75 # "latest" is the version its CI overwrites on every default-branch build,
76 # so this input finds a new one on `nix flake update` -- but flake.lock
77 # still records exactly which bytes an APK was built from, which is the
78 # pin that matters. `just bump` decides when to move; this only decides
79 # where to look. The archive is rooted at include/ and lib/arm64-v8a/, so
80 # nothing here has to unpack it.
81 jolt-native-android = {
82 url = "https://gitlab.com/api/v4/projects/nandithebull%2Fjolt-native/packages/generic/jolt-native/latest/android-arm64-v8a.tar.gz";
83 flake = false;
84 };
85
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago86 # Chez itself, because the APK needs a cross target nixpkgs does not
87 # build: frq's Scheme is compiled to an arm64 boot image, and that wants
88 # Chez's own `tarm64le` workarea — boot files, xpatch and libkernel.a.
89 # The version is the one the hand-built tree under ~/.cache used, and the
90 # submodules are not optional (zuo builds it, lz4 and zlib link into it).
Take jolt and jolt-native as flakes, off my own fork aaa560d nandi 13d ago91 # The same fork jolt-src takes, built here rather than fetched as a
92 # release binary: upstream reads the socket address out of `struct
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago93 # addrinfo` at glibc's offset, which on Bionic is `ai_canonname`, so an APK
Take jolt and jolt-native as flakes, off my own fork aaa560d nandi 13d ago94 # built with upstream cannot open a TLS connection at all. Pinned to a rev
95 # where jolt-src is not: the APK is a release artefact, so its runtime
96 # moves when `just bump` says so rather than when the fork does.
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago97 jolt-android-src = {
98 url = "git+https://gitlab.com/nandithebull/jolt?rev=2b80d68d1f7a31ba92b208b3957e5fb555617ada&submodules=1";
Take jolt and jolt-native as flakes, off my own fork aaa560d nandi 13d ago99 inputs.nixpkgs.follows = "nixpkgs";
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago100 };
101
102 chez-src = {
103 url = "git+https://github.com/cisco/ChezScheme?ref=refs/tags/v10.4.1&submodules=1";
104 flake = false;
105 };
106
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago107 # The sha deps.edn pins, on the fork with the reconciler fixes.
108 glimmer = {
109 url = "git+https://gitlab.com/nandithebull/glimmer?rev=399df371c790d690fb6e4560c3d4d7f838502857";
110 flake = false;
111 };
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago112
113 # Only ever used off NixOS, to put the host GL driver on the loader path.
114 nixgl = {
115 url = "github:nix-community/nixGL";
116 inputs.nixpkgs.follows = "nixpkgs";
117 };
Bundle the closure into one file the AppImage runtime mounts f82b93f nandi 18d ago118
119 # Wraps a closure into a single self-extracting file. Only the `appimage`
120 # output evaluates it.
121 nix-appimage = {
122 url = "github:ralismark/nix-appimage";
123 inputs.nixpkgs.follows = "nixpkgs";
124 };
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago125 };
126
Fetch libmoq_ffi again, and keep the codecs out of Rust f518938 nandi 9d ago127 outputs = { self, nixpkgs, jolt-src, jolt-native, jolt-native-android, glimmer, chez-src, jolt-android-src, nixgl, nix-appimage }:
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago128 let
129 systems = [ "x86_64-linux" "aarch64-linux" ];
130 forEachSystem = f:
131 nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
Run this tree on a native half the builders made a32699e nandi 17d ago132
133 # Mesa, despite the name: it covers Intel and AMD alike. The NVIDIA
134 # wrappers are the ones that need --impure (they read the host kernel
135 # 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 17d ago136 #
137 # Built from nixGL's default.nix rather than taken from its flake
138 # outputs, for the one argument the flake hardcodes on: `enable32bits`,
139 # which on x86_64 puts a second, i686 copy of mesa, its LLVM, and
140 # intel-media-driver into the wrapper. frq is 64-bit on both halves —
141 # the Rust cdylibs and the Chez runtime — so nothing here ever opens the
142 # 32-bit driver, and carrying it is most of the dev shell's closure.
143 nixGLFor = pkgs: (import nixgl {
144 inherit pkgs;
145 enable32bits = false;
146 }).nixGLIntel;
Run this tree on a native half the builders made a32699e nandi 17d ago147
148 # egui reaches for these with dlopen rather than linking them, so being
149 # 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 ago150 # 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 ago151 # "X11 unavailable", falls back to Wayland, and winit refuses to build a
152 # second event loop after the failed first one.
153 #
154 # Out here rather than beside the package that first needed them: the
155 # dev shell starts frq too, on this tree's source rather than the store's
156 # copy of it, and a second copy of this list is a second chance for the
157 # two ways of running to disagree about what the window needs.
158 runtimeLibsFor = pkgs: with pkgs; [
159 libGL
160 libxkbcommon
161 wayland
Follow the xorg renames, and stop wrapping nix in distrobox a400a00 nandi 16d ago162 libx11
163 libxcursor
164 libxi
165 libxrandr
Run this tree on a native half the builders made a32699e nandi 17d ago166 vulkan-loader
167 ];
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago168 in
169 {
170 packages = forEachSystem (pkgs:
171 let
172 inherit (pkgs) lib;
173
Run this tree on a native half the builders made a32699e nandi 17d ago174 nixGL = nixGLFor pkgs;
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago175
Take the native objects from their own flake rather than rebuilding them 0ee8bba nandi 13d ago176 # libvidya (the retained-tree ABI glimmer-vidya binds, on egui),
177 # libjolttui (the same tree over a grid of cells) and libjoltmoq (the
178 # AV media plane) — one workspace, three cdylibs, taken from
179 # jolt-native's own flake rather than rebuilt here.
180 #
181 # This used to be a rustPlatform.buildRustPackage over the same
182 # source, which meant restating upstream's build: the seven git deps
183 # hashed by hand in `cargoLock.outputHashes` and re-hashed whenever
184 # its Cargo.lock moved, the linuxHeaders path v4l2r's bindgen wants,
185 # and a postPatch dropping the .cargo/config.toml that pointed the
186 # build at DotSlash. Upstream's flake says all of that itself now,
187 # and says it once. It also builds cpal with the `pipewire` feature,
188 # which the restatement did not — so device names in a call are
189 # PipeWire's rather than raw ALSA PCMs.
Switch frq.av onto the jolt media plane, and stop shipping libjoltmoq b33f288 nandi 9d ago190 # libvidya and libjolttui, NOT libjoltmoq. Its `default` is all
191 # three joined, and the third is the Rust media plane frq no
192 # longer loads — `frq.av.plane` replaced it.
193 #
194 # This makes the closure smaller and the APK smaller. It does NOT
195 # make the build shorter, and it is worth being exact about why:
196 # jolt-native compiles its external crates ONCE, in a
197 # `buildDepsOnly` derivation shared by all three objects, so
198 # asking for two of them still builds every dependency the third
199 # has — the 440 crates that are jolt-moq's alone. Getting those
200 # out of the build is a change in jolt-native, not here: either
201 # jolt-moq leaves that workspace, or its deps artifact stops
202 # being workspace-wide.
203 native =
204 let np = jolt-native.packages.${pkgs.stdenv.hostPlatform.system};
205 in pkgs.symlinkJoin {
206 name = "jolt-native-ui";
207 paths = [ np.libvidya np.libjolttui ];
208 };
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago209
Fetch libmoq_ffi again, and keep the codecs out of Rust f518938 nandi 9d ago210 # libmoq_ffi — MoQ over QUIC behind UniFFI's C ABI, FETCHED rather
211 # 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 ago212 #
Fetch libmoq_ffi again, and keep the codecs out of Rust f518938 nandi 9d ago213 # Fetched because building it is the thing this whole exercise is
214 # about: moq-ffi pulls moq-native, iroh, quinn, rustls and aws-lc-sys
215 # behind it, which is 440 crates that nothing else in this tree
216 # needs. Upstream already publishes the object for both Linux
217 # architectures, so we take those bytes.
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago218 #
Fetch libmoq_ffi again, and keep the codecs out of Rust f518938 nandi 9d ago219 # Pinned to a release and to a hash, and the hashes below are
220 # upstream's own published .sha256 files rather than ones observed
221 # here — a `nix-prefetch` of whatever the URL serves today would
222 # record that it downloaded something, not that it downloaded the
223 # right thing.
224 #
225 # WHAT THIS BUILD IS NOT: moq-ffi's `audio` and `video` features are
226 # on by default upstream and are OFF in these artifacts, so there is
227 # no publish_audio/publish_video and no moqaudio*/moqvideo* here —
228 # 206 functions where the Apple artifact has 230. That is why the
229 # bindings are generated from the object (`just gen-moq`) and not
230 # from the C header the release ships, which describes the Apple one.
231 moqFfi =
232 let
233 version = "0.3.17";
234 target = {
235 "x86_64-linux" = "x86_64-unknown-linux-gnu";
236 "aarch64-linux" = "aarch64-unknown-linux-gnu";
237 }.${pkgs.stdenv.hostPlatform.system};
238 hash = {
239 "x86_64-linux" = "sha256-dzQXpV4JgdtD+g33WX51FFAQdfCUXkNsx1xPbobPfUI=";
240 "aarch64-linux" = "sha256-PdzRwbJFqOZWRgI0HHX2XUH+Ljh4V3jvQ9asfvCuIPA=";
241 }.${pkgs.stdenv.hostPlatform.system};
242 in
243 pkgs.stdenv.mkDerivation {
244 pname = "libmoq-ffi";
245 inherit version;
246 src = pkgs.fetchurl {
247 url = "https://github.com/kixelated/moq/releases/download/moq-ffi-v${version}/moq-ffi-${version}-${target}-libmoq_ffi.so";
248 inherit hash;
249 };
250 dontUnpack = true;
251 # It carries no RUNPATH and needs libgcc_s, libm and libc — the
252 # host's on an ordinary distro, and nothing at all on NixOS
253 # unless they are bound here.
254 nativeBuildInputs = [ pkgs.autoPatchelfHook ];
255 buildInputs = [ pkgs.stdenv.cc.cc.lib ];
256 installPhase = ''
257 mkdir -p $out/lib
258 cp $src $out/lib/libmoq_ffi.so
259 chmod +w $out/lib/libmoq_ffi.so
260 '';
261 };
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago262
263 # One directory for the loader to look in. jolt resolves every
264 # :jolt/native name against JOLT_NATIVE_LIB, and the objects now come
265 # from two places — jolt-native's flake, and the moq-ffi release — so
266 # they are joined rather than the path being made a list, which the
267 # loader does not take.
Bind the C libraries a call actually needs 87e5be9 nandi 9d ago268 # The C codecs, from nixpkgs. libmoq_ffi carries the transport and
269 # nothing else — moq-ffi's `audio` and `video` features would have
270 # brought Opus and H.264 with them, at the price of compiling a
271 # 1062-crate workspace — so the codecs are linked here instead,
272 # where they have always lived.
273 #
274 # Named in :jolt/native, so the loader resolves them the same way it
275 # resolves libvidya: by name, out of one directory.
276 # A flat C face for openh264, because openh264 has none. Its
277 # `ISVCEncoder` is `const ISVCEncoderVtbl*` — every method is a
278 # function pointer in a vtable — and jolt.ffi cannot call one: Chez
279 # fixes a foreign procedure's types when it compiles it, and the
280 # target must be a literal C symbol name. So the vtable is walked in
281 # c/frq_h264.c and jolt binds the five plain symbols it exports.
282 #
283 # One translation unit against a library nixpkgs already has. It is
284 # a calling convention adapter, not a second media plane, and the
285 # distinction from the moq-ffi build it replaces is the whole point:
286 # this compiles one .c file, not a 1062-crate workspace.
287 frqH264 = pkgs.stdenv.mkDerivation {
288 pname = "frq-h264";
289 version = "0.1";
290 src = ./c;
291 nativeBuildInputs = [ pkgs.pkg-config ];
292 buildInputs = [ pkgs.openh264 ];
293 buildPhase = ''
294 $CC -O2 -fPIC -shared frq_h264.c -o libfrqh264.so \
295 $(pkg-config --cflags --libs openh264)
296 '';
297 installPhase = ''
298 mkdir -p $out/lib && cp libfrqh264.so $out/lib/
299 '';
300 };
301
302 # openh264 is here for frqH264's DT_NEEDED; alsa-lib for capture
303 # and playback. V4L2 needs nothing: it is ioctls against libc and
304 # the kernel, so there is no library to name.
305 codecs = [ pkgs.libopus pkgs.openh264 frqH264 pkgs.alsa-lib ];
306
Let the plane take real devices, not only test thunks 596dc29 nandi 9d ago307 # ALSA's PipeWire plugin, which is how `default` resolves to
308 # anything on a machine running PipeWire — and every machine frq
309 # targets does. Without it alsa-lib fails to dlopen
310 # libasound_module_pcm_pipewire.so and the only devices that open
311 # are raw hardware ones, which PipeWire is already holding.
312 #
313 # An environment variable rather than a library in the join:
314 # alsa-lib looks plugins up by directory, not by soname.
315 alsaPluginDir = "${pkgs.pipewire}/lib/alsa-lib";
316
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago317 nativeAll = pkgs.symlinkJoin {
318 name = "frq-native";
Bind the C libraries a call actually needs 87e5be9 nandi 9d ago319 paths = [ native moqFfi ] ++ codecs;
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago320 };
321
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago322 # 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 ago323 # A function, because there are two of them — upstream for the
324 # desktop, and the Bionic-addrinfo fork for the boot image the APK
325 # carries. Nothing else about the build differs.
326 joltFrom = src: pkgs.stdenv.mkDerivation {
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago327 pname = "jolt";
328 version = "dev";
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago329 inherit src;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago330
331 strictDeps = true;
332 nativeBuildInputs = with pkgs; [ chez makeWrapper pkg-config xxd ];
333 buildInputs = with pkgs; [ lz4 zlib ncurses openssl libuuid ];
334
335 JOLT_VERSION = "dev";
336 dontConfigure = true;
337
338 buildPhase = ''
339 runHook preBuild
340 scheme --script host/chez/build-jolt.ss release target/release/jolt
341 runHook postBuild
342 '';
343
344 installPhase = ''
345 runHook preInstall
346 mkdir -p "$out/bin"
347 install -m755 target/release/jolt "$out/bin/jolt"
348 runHook postInstall
349 '';
350
351 # 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 17d ago352 # OpenSSL through the JOLT_OPENSSL_LIBDIR seam. gitMinimal rather
353 # than git: all jolt.deps asks for is clone/fetch/rev-parse, and
354 # the full package carries Perl and Python for the subcommands
355 # 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 ago356 #
357 # TZDIR so a zone *name* resolves wherever this runs: frq.clock
358 # hands one to tzset, and glibc then looks for the tzfile under
359 # /usr/share/zoneinfo unless told otherwise — which a NixOS host
360 # does not have. The store's own tzdata is there on both kinds of
361 # 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 ago362 postFixup = ''
363 wrapProgram "$out/bin/jolt" \
Stop carrying the 32-bit GL stack and half of git 040f2c1 nandi 17d ago364 --prefix PATH : "${pkgs.lib.makeBinPath [ pkgs.gitMinimal pkgs.unzip ]}" \
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago365 --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 ago366 --set-default TZDIR "${pkgs.tzdata}/share/zoneinfo" \
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago367 --set-default SSL_CERT_FILE "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
368 '';
369 };
370
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago371 joltRuntime = joltFrom jolt-src;
372 joltAndroid = joltFrom jolt-android-src;
373
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago374 # glimmer-vidya lives inside the jolt-native checkout, and its own
375 # deps.edn asks for glimmer by git — the top-level override below
376 # answers for both.
Take the Android objects from jolt-native's CI, always the latest 472e179 nandi 11d ago377 glimmerVidya = "${jolt-native}/glimmer-backends/glimmer-vidya";
378 glimmerTui = "${jolt-native}/glimmer-backends/glimmer-tui";
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago379
Run this tree on a native half the builders made a32699e nandi 17d ago380 runtimeLibs = runtimeLibsFor pkgs;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago381
382 # The project as jolt sees it: source, deps.edn, nothing else.
383 frqSource = pkgs.runCommand "frq-source" { } ''
384 mkdir -p "$out"
385 cp -r ${self}/src ${self}/deps.edn "$out/"
386 '';
387
388 # Jolt resolves deps.edn from the working directory, so the launcher
389 # runs from the store copy. Its .jolt/cpcache write lands on a
390 # read-only directory and jolt treats that as a quiet cache miss, so
391 # the only cost is re-resolving the (already local) graph per start.
392 frqScript = pkgs.writeShellScript "frq" ''
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago393 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 ago394 export ALSA_PLUGIN_DIR="${alsaPluginDir}"
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago395 cd ${frqSource}
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago396
397 # On NixOS the store's Mesa is the system's and the window opens.
398 # Anywhere else the real driver is the host's, so defer to nixGL
399 # it prepends the host driver, which has to win over ours.
400 runner=""
401 [ -e /run/current-system ] || runner="${nixGL}/bin/nixGLIntel"
402
403 exec ''${runner} ${joltRuntime}/bin/jolt \
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago404 -Sdeps '{:deps {jolt-lang/glimmer {:local/root "${glimmer}"} nandi/glimmer-vidya {:local/root "${glimmerVidya}"}}}' \
405 -M:frq "$@"
406 '';
407
Paint the same screens into a terminal ab83b42 nandi 17d ago408 # The same source, the other backend. No GL, no nixGL and no X11 —
409 # a terminal is the one surface that needs nothing from the host but
410 # a terminal, which is the reason this output exists.
411 tuiScript = pkgs.writeShellScript "frq-tui" ''
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago412 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 ago413 export ALSA_PLUGIN_DIR="${alsaPluginDir}"
Paint the same screens into a terminal ab83b42 nandi 17d ago414 cd ${frqSource}
415
416 exec ${joltRuntime}/bin/jolt \
417 -Sdeps '{:deps {jolt-lang/glimmer {:local/root "${glimmer}"} nandi/glimmer-vidya {:local/root "${glimmerVidya}"} nandi/glimmer-tui {:local/root "${glimmerTui}"}}}' \
418 -m frq.tui "$@"
419 '';
420
421 tui = pkgs.runCommand "frq-tui-0.1.0"
422 {
423 meta = {
424 description = "frq's screens in a terminal";
425 mainProgram = "frq-tui";
426 platforms = systems;
427 };
428 }
429 ''
430 mkdir -p "$out/bin"
431 ln -s ${tuiScript} "$out/bin/frq-tui"
432 '';
433
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago434 frq = pkgs.runCommand "frq-0.1.0"
435 {
436 meta = {
437 description = "A freeq client in jolt";
438 mainProgram = "frq";
439 platforms = systems;
440 };
441 }
442 ''
443 mkdir -p "$out/bin"
444 ln -s ${frqScript} "$out/bin/frq"
445 '';
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago446 # --- Android ------------------------------------------------------
447 # The SDK and the NDK are Google's, which means unfree and a licence
448 # to accept — so this is its own import of nixpkgs rather than the
449 # `legacyPackages` everything above uses. Confined to the Android
450 # outputs: `nix build` of frq itself never evaluates it.
451 #
Build the APK as derivations only, and retire the buck2 graph 3bc3aaa nandi 16d ago452 # The NDK here is r29, which is the one the pinned libvidya was
453 # built with.
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago454 androidPkgs = import nixpkgs {
455 inherit (pkgs.stdenv.hostPlatform) system;
456 config = {
457 allowUnfree = true;
458 android_sdk.accept_license = true;
459 };
460 };
461
462 androidComposition = androidPkgs.androidenv.composeAndroidPackages {
463 buildToolsVersions = [ "36.0.0" ];
464 platformVersions = [ "36" ];
465 includeNDK = true;
466 };
467
468 android = import ./nix/android.nix {
Take the Android objects from jolt-native's CI, always the latest 472e179 nandi 11d ago469 inherit pkgs self chez-src jolt-native jolt-native-android glimmer joltAndroid;
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago470 inherit (pkgs) lib;
471 androidSdk = androidComposition.androidsdk;
472 ndk = androidComposition.ndk-bundle;
473 };
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago474 in
475 {
Bind the C libraries a call actually needs 87e5be9 nandi 9d ago476 inherit native moqFfi frqH264 nativeAll frq;
Let the plane take real devices, not only test thunks 596dc29 nandi 9d ago477 inherit (pkgs) pipewire;
Merge origin/main, and let one jolt-native answer for both backends 1e8b590 nandi 16d ago478 inherit tui;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago479 jolt = joltRuntime;
480 default = frq;
Bundle the closure into one file the AppImage runtime mounts f82b93f nandi 18d ago481
482 # frq and everything it loads, squashed into one runnable file for
483 # hosts without Nix. The whole closure rides along — Mesa included,
484 # which is not waste: off NixOS the launcher goes through nixGL, and
485 # nixGL needs a store Mesa to put the host's driver in front of.
486 appimage =
487 nix-appimage.bundlers.${pkgs.stdenv.hostPlatform.system}.default frq;
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago488 }
489 # An APK is built by a linux-x86_64 NDK and a linux-x86_64 jolt, and
490 # Google ships no other; on aarch64 the Android outputs are simply
491 # absent rather than present and broken.
492 // lib.optionalAttrs (pkgs.stdenv.hostPlatform.system == "x86_64-linux") {
493 inherit (android) apk chezAndroid joltBoot libjoltapp;
494 apk-unsigned = android.apk-unsigned;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago495 });
496
Run this tree on a native half the builders made a32699e nandi 17d ago497 # Where `just run` runs, and — because entering it realises what it
498 # names — what builds the half of frq that is not this working tree.
499 #
500 # The two halves, and the split is the whole point of the shell. The frq
501 # source is the files on disk, uncommitted edits and all. Everything
502 # under it — jolt, glimmer, glimmer-vidya, both native objects — is the
503 # flake's, at the revs flake.lock names, so a run says what it ran
504 # against and both halves of glimmer-vidya move together. That is the
505 # drift the `jolt-native` input's comment is about, and a pin frq can
506 # answer for is worth more here than the convenience of a checkout.
507 #
Take the native objects from their own flake rather than rebuilding them 0ee8bba nandi 13d ago508 # `native` is jolt-native's own flake output. It was a buck2 graph when
509 # this comment was first written and a cargo build restated here when it
510 # was second: buck2 fetches its rustc, zig and every third-party crate as
511 # it goes and writes buck-out into the tree it builds, so a sandbox with
512 # no network and a read-only store was the one place it could not run.
513 # Upstream builds with nix now, so the thing its CI runs and the thing
514 # this shell hands a builder are the same derivation.
Run this tree on a native half the builders made a32699e nandi 17d ago515 #
516 # Nothing here says "nixbuild", though: it is a plain derivation, and
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago517 # 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 ago518 # the shell with --max-jobs 0, which is what sends it to the `builders`
519 # entry rather than compiling egui on a laptop.
520 devShells = forEachSystem (pkgs:
521 let
522 inherit (pkgs) lib;
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago523 inherit (self.packages.${pkgs.stdenv.hostPlatform.system}) jolt native nativeAll;
Run this tree on a native half the builders made a32699e nandi 17d ago524 in
525 {
526 default = pkgs.mkShellNoCC {
527 name = "frq";
528
529 # jolt, because the runtime frq is run by should be the flake's
530 # too. nixGL for the same reason the launcher reaches for it — see
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago531 # frqScript. just so the recipe runner comes from here too rather
532 # than the host — `nix develop` and then `just run` is the whole of
533 # what a machine with nix needs.
534 packages = [ jolt pkgs.just (nixGLFor pkgs) ];
535
536 # 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 ago537 # source `just run` runs is the working tree, so the launcher has
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago538 # to live in that tree and the shell has to hand it its answers.
539 # 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 ago540 JOLT_NATIVE_LIB = "${nativeAll}/lib";
Let the plane take real devices, not only test thunks 596dc29 nandi 9d ago541 # Spelled out rather than shared with the packages block, which
542 # is a different `let`. See `alsaPluginDir` there for why.
543 ALSA_PLUGIN_DIR = "${pkgs.pipewire}/lib/alsa-lib";
Run this tree on a native half the builders made a32699e nandi 17d ago544 GLIMMER_SRC = glimmer;
Take the Android objects from jolt-native's CI, always the latest 472e179 nandi 11d ago545 GLIMMER_VIDYA_SRC = "${jolt-native}/glimmer-backends/glimmer-vidya";
546 GLIMMER_TUI_SRC = "${jolt-native}/glimmer-backends/glimmer-tui";
Run this tree on a native half the builders made a32699e nandi 17d ago547 FRQ_LIB_PATH = lib.makeLibraryPath (runtimeLibsFor pkgs);
548 NIXGL = "${nixGLFor pkgs}/bin/nixGLIntel";
549 };
550 });
551
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago552 apps = forEachSystem (pkgs: {
553 default = {
554 type = "app";
555 program = "${self.packages.${pkgs.stdenv.hostPlatform.system}.frq}/bin/frq";
556 };
Paint the same screens into a terminal ab83b42 nandi 17d ago557 tui = {
558 type = "app";
559 program = "${self.packages.${pkgs.stdenv.hostPlatform.system}.tui}/bin/frq-tui";
560 };
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago561 });
562 };
563}