nandi/frqpublic Fork 0
36f6c77d9a7dcfa050a3394893c6fe4867ddbc2c
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 · 378 lines · 16.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";
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.
97 nixGLFor = pkgs: nixgl.packages.${pkgs.stdenv.hostPlatform.system}.nixGLIntel;
98
99 # egui reaches for these with dlopen rather than linking them, so being
100 # in the cdylib's buildInputs is not enough — whatever starts frq has to
101 # put them on the loader path itself. Without libX11 here, vidya reports
102 # "X11 unavailable", falls back to Wayland, and winit refuses to build a
103 # second event loop after the failed first one.
104 #
105 # Out here rather than beside the package that first needed them: the
106 # dev shell starts frq too, on this tree's source rather than the store's
107 # copy of it, and a second copy of this list is a second chance for the
108 # two ways of running to disagree about what the window needs.
109 runtimeLibsFor = pkgs: with pkgs; [
110 libGL
111 libxkbcommon
112 wayland
113 xorg.libX11
114 xorg.libXcursor
115 xorg.libXi
116 xorg.libXrandr
117 vulkan-loader
118 ];
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago119 in
120 {
121 packages = forEachSystem (pkgs:
122 let
123 inherit (pkgs) lib;
124
Run this tree on a native half the builders made a32699e nandi 17d ago125 nixGL = nixGLFor pkgs;
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago126
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago127 # libvidya (the retained-tree ABI glimmer-vidya binds, on egui) and
128 # libjoltmoq (the AV media plane). One workspace, two cdylibs.
129 native = pkgs.rustPlatform.buildRustPackage {
130 pname = "jolt-native";
131 version = "0.1.0";
132 src = jolt-native;
133
134 cargoLock = {
135 lockFile = "${jolt-native}/Cargo.lock";
136 allowBuiltinFetchGit = true;
137 };
138
139 nativeBuildInputs = with pkgs; [
140 pkg-config
141 cmake
142 rustPlatform.bindgenHook
143 ];
144
145 buildInputs = with pkgs; [
146 alsa-lib
147 pipewire
148 openssl
149 libxkbcommon
150 wayland
151 libGL
152 ];
153
154 # Upstream's .cargo/config.toml drives the whole build through
155 # DotSlash: a pinned rustc, sccache, and zig as the C/C++ compiler
156 # and linker, each fetched from the network on first use. None of
157 # that survives a build sandbox, and none of it is needed when the
158 # toolchain comes from the store — so drop it and let stdenv's cc
159 # link (openh264 is C++, which stdenv covers too).
160 postPatch = ''
161 rm -f .cargo/config.toml
162 '';
163
164 # bindgen reads linux/videodev2.h directly; upstream points it at
165 # zig's bundled headers, which under Nix is just the kernel headers.
166 V4L2R_VIDEODEV2_H_PATH = "${pkgs.linuxHeaders}/include";
167
168 doCheck = false;
169
170 installPhase = ''
171 runHook preInstall
172 mkdir -p "$out/lib"
173 install -m644 target/*/release/*.so "$out/lib/"
174 runHook postInstall
175 '';
176 };
177
178 # 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 ago179 # A function, because there are two of them — upstream for the
180 # desktop, and the Bionic-addrinfo fork for the boot image the APK
181 # carries. Nothing else about the build differs.
182 joltFrom = src: pkgs.stdenv.mkDerivation {
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago183 pname = "jolt";
184 version = "dev";
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago185 inherit src;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago186
187 strictDeps = true;
188 nativeBuildInputs = with pkgs; [ chez makeWrapper pkg-config xxd ];
189 buildInputs = with pkgs; [ lz4 zlib ncurses openssl libuuid ];
190
191 JOLT_VERSION = "dev";
192 dontConfigure = true;
193
194 buildPhase = ''
195 runHook preBuild
196 scheme --script host/chez/build-jolt.ss release target/release/jolt
197 runHook postBuild
198 '';
199
200 installPhase = ''
201 runHook preInstall
202 mkdir -p "$out/bin"
203 install -m755 target/release/jolt "$out/bin/jolt"
204 runHook postInstall
205 '';
206
207 # jolt.deps shells out to git and unzip, and jolt.mvn-http dlopens
208 # OpenSSL through the JOLT_OPENSSL_LIBDIR seam.
Read the clock in the reader's zone when /etc/localtime is a file bffe879 nandi 18d ago209 #
210 # TZDIR so a zone *name* resolves wherever this runs: frq.clock
211 # hands one to tzset, and glibc then looks for the tzfile under
212 # /usr/share/zoneinfo unless told otherwise — which a NixOS host
213 # does not have. The store's own tzdata is there on both kinds of
214 # 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 ago215 postFixup = ''
216 wrapProgram "$out/bin/jolt" \
217 --prefix PATH : "${pkgs.lib.makeBinPath [ pkgs.git pkgs.unzip ]}" \
218 --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 ago219 --set-default TZDIR "${pkgs.tzdata}/share/zoneinfo" \
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago220 --set-default SSL_CERT_FILE "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
221 '';
222 };
223
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago224 joltRuntime = joltFrom jolt-src;
225 joltAndroid = joltFrom jolt-android-src;
226
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago227 # glimmer-vidya lives inside the jolt-native checkout, and its own
228 # deps.edn asks for glimmer by git — the top-level override below
229 # answers for both.
230 glimmerVidya = "${jolt-native}/jolt/glimmer-vidya";
231
Run this tree on a native half the builders made a32699e nandi 17d ago232 runtimeLibs = runtimeLibsFor pkgs;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago233
234 # The project as jolt sees it: source, deps.edn, nothing else.
235 frqSource = pkgs.runCommand "frq-source" { } ''
236 mkdir -p "$out"
237 cp -r ${self}/src ${self}/deps.edn "$out/"
238 '';
239
240 # Jolt resolves deps.edn from the working directory, so the launcher
241 # runs from the store copy. Its .jolt/cpcache write lands on a
242 # read-only directory and jolt treats that as a quiet cache miss, so
243 # the only cost is re-resolving the (already local) graph per start.
244 frqScript = pkgs.writeShellScript "frq" ''
245 export LD_LIBRARY_PATH="${native}/lib:${lib.makeLibraryPath runtimeLibs}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
246 cd ${frqSource}
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago247
248 # On NixOS the store's Mesa is the system's and the window opens.
249 # Anywhere else the real driver is the host's, so defer to nixGL
250 # it prepends the host driver, which has to win over ours.
251 runner=""
252 [ -e /run/current-system ] || runner="${nixGL}/bin/nixGLIntel"
253
254 exec ''${runner} ${joltRuntime}/bin/jolt \
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago255 -Sdeps '{:deps {jolt-lang/glimmer {:local/root "${glimmer}"} nandi/glimmer-vidya {:local/root "${glimmerVidya}"}}}' \
256 -M:frq "$@"
257 '';
258
259 frq = pkgs.runCommand "frq-0.1.0"
260 {
261 meta = {
262 description = "A freeq client in jolt";
263 mainProgram = "frq";
264 platforms = systems;
265 };
266 }
267 ''
268 mkdir -p "$out/bin"
269 ln -s ${frqScript} "$out/bin/frq"
270 '';
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago271 # --- Android ------------------------------------------------------
272 # The SDK and the NDK are Google's, which means unfree and a licence
273 # to accept — so this is its own import of nixpkgs rather than the
274 # `legacyPackages` everything above uses. Confined to the Android
275 # outputs: `nix build` of frq itself never evaluates it.
276 #
277 # The NDK here is r29, which is the version scripts/android-ndk.dotslash
278 # pins and the one the pinned libvidya was built with.
279 androidPkgs = import nixpkgs {
280 inherit (pkgs.stdenv.hostPlatform) system;
281 config = {
282 allowUnfree = true;
283 android_sdk.accept_license = true;
284 };
285 };
286
287 androidComposition = androidPkgs.androidenv.composeAndroidPackages {
288 buildToolsVersions = [ "36.0.0" ];
289 platformVersions = [ "36" ];
290 includeNDK = true;
291 };
292
293 android = import ./nix/android.nix {
294 inherit pkgs self chez-src jolt-native glimmer joltAndroid;
295 inherit (pkgs) lib;
296 androidSdk = androidComposition.androidsdk;
297 ndk = androidComposition.ndk-bundle;
298 };
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago299 in
300 {
301 inherit native frq;
302 jolt = joltRuntime;
303 default = frq;
Bundle the closure into one file the AppImage runtime mounts f82b93f nandi 18d ago304
305 # frq and everything it loads, squashed into one runnable file for
306 # hosts without Nix. The whole closure rides along — Mesa included,
307 # which is not waste: off NixOS the launcher goes through nixGL, and
308 # nixGL needs a store Mesa to put the host's driver in front of.
309 appimage =
310 nix-appimage.bundlers.${pkgs.stdenv.hostPlatform.system}.default frq;
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago311 }
312 # An APK is built by a linux-x86_64 NDK and a linux-x86_64 jolt, and
313 # Google ships no other; on aarch64 the Android outputs are simply
314 # absent rather than present and broken.
315 // lib.optionalAttrs (pkgs.stdenv.hostPlatform.system == "x86_64-linux") {
316 inherit (android) apk chezAndroid joltBoot libjoltapp;
317 apk-unsigned = android.apk-unsigned;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago318 });
319
Run this tree on a native half the builders made a32699e nandi 17d ago320 # Where `just run` runs, and — because entering it realises what it
321 # names — what builds the half of frq that is not this working tree.
322 #
323 # The two halves, and the split is the whole point of the shell. The frq
324 # source is the files on disk, uncommitted edits and all. Everything
325 # under it — jolt, glimmer, glimmer-vidya, both native objects — is the
326 # flake's, at the revs flake.lock names, so a run says what it ran
327 # against and both halves of glimmer-vidya move together. That is the
328 # drift the `jolt-native` input's comment is about, and a pin frq can
329 # answer for is worth more here than the convenience of a checkout.
330 #
331 # `native` is the cargo build of that input rather than jolt-native's own
332 # buck2 graph, which is a compromise and not a free one: buck2 is what
333 # its CI runs and what makes its releases, and its cpal has the pipewire
334 # feature this one does not, so device *names* in a call come out as ALSA
335 # PCMs. What it buys is a derivation — one thing nixbuild.net can be
336 # handed. The buck2 build fetches its rustc, zig and every third-party
337 # crate as it goes and writes buck-out into the tree it builds; a sandbox
338 # with no network and a read-only store is the one place it cannot run,
339 # so on a remote builder it is not a slower option but no option at all.
340 #
341 # Nothing here says "nixbuild", though: it is a plain derivation, and
342 # where it gets built is the machine's business. scripts/run.bb asks for
343 # the shell with --max-jobs 0, which is what sends it to the `builders`
344 # entry rather than compiling egui on a laptop.
345 devShells = forEachSystem (pkgs:
346 let
347 inherit (pkgs) lib;
348 inherit (self.packages.${pkgs.stdenv.hostPlatform.system}) jolt native;
349 in
350 {
351 default = pkgs.mkShellNoCC {
352 name = "frq";
353
354 # jolt, because the runtime frq is run by should be the flake's
355 # too. nixGL for the same reason the launcher reaches for it — see
356 # frqScript.
357 packages = [ jolt (nixGLFor pkgs) ];
358
359 # Read by scripts/run.bb rather than baked into a wrapper: the frq
360 # source `just run` runs is the working tree, so the launcher has
361 # to be a script in that tree and the shell has to hand it its
362 # answers. Naming these is also what makes the shell build them.
363 JOLT_NATIVE_LIB = "${native}/lib";
364 GLIMMER_SRC = glimmer;
365 GLIMMER_VIDYA_SRC = "${jolt-native}/jolt/glimmer-vidya";
366 FRQ_LIB_PATH = lib.makeLibraryPath (runtimeLibsFor pkgs);
367 NIXGL = "${nixGLFor pkgs}/bin/nixGLIntel";
368 };
369 });
370
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago371 apps = forEachSystem (pkgs: {
372 default = {
373 type = "app";
374 program = "${self.packages.${pkgs.stdenv.hostPlatform.system}.frq}/bin/frq";
375 };
376 });
377 };
378}