nandi/frqpublic Fork 0
a32699ed51bfbcc605737c045b0f1b5673a05a7c
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 · 374 lines · 16.1 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
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago44 # Chez itself, because the APK needs a cross target nixpkgs does not
45 # build: frq's Scheme is compiled to an arm64 boot image, and that wants
46 # Chez's own `tarm64le` workarea — boot files, xpatch and libkernel.a.
47 # The version is the one the hand-built tree under ~/.cache used, and the
48 # submodules are not optional (zuo builds it, lz4 and zlib link into it).
49 # The fork jolt's own Android pin names, built here rather than fetched as
50 # a release binary: upstream reads the socket address out of `struct
51 # addrinfo` at glibc's offset, which on Bionic is `ai_canonname`, so an APK
52 # built with upstream cannot open a TLS connection at all. Only the boot
53 # image uses it; the desktop package still builds jolt-src.
54 jolt-android-src = {
55 url = "git+https://gitlab.com/nandithebull/jolt?rev=2b80d68d1f7a31ba92b208b3957e5fb555617ada&submodules=1";
56 flake = false;
57 };
58
59 chez-src = {
60 url = "git+https://github.com/cisco/ChezScheme?ref=refs/tags/v10.4.1&submodules=1";
61 flake = false;
62 };
63
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago64 # The sha deps.edn pins, on the fork with the reconciler fixes.
65 glimmer = {
66 url = "git+https://gitlab.com/nandithebull/glimmer?rev=399df371c790d690fb6e4560c3d4d7f838502857";
67 flake = false;
68 };
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago69
70 # Only ever used off NixOS, to put the host GL driver on the loader path.
71 nixgl = {
72 url = "github:nix-community/nixGL";
73 inputs.nixpkgs.follows = "nixpkgs";
74 };
Bundle the closure into one file the AppImage runtime mounts f82b93f nandi 18d ago75
76 # Wraps a closure into a single self-extracting file. Only the `appimage`
77 # output evaluates it.
78 nix-appimage = {
79 url = "github:ralismark/nix-appimage";
80 inputs.nixpkgs.follows = "nixpkgs";
81 };
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago82 };
83
Bundle the closure into one file the AppImage runtime mounts f82b93f nandi 18d ago84 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 ago85 let
86 systems = [ "x86_64-linux" "aarch64-linux" ];
87 forEachSystem = f:
88 nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
Run this tree on a native half the builders made a32699e nandi 17d ago89
90 # Mesa, despite the name: it covers Intel and AMD alike. The NVIDIA
91 # wrappers are the ones that need --impure (they read the host kernel
92 # module's version), which is why this only ever reaches for Intel.
93 nixGLFor = pkgs: nixgl.packages.${pkgs.stdenv.hostPlatform.system}.nixGLIntel;
94
95 # egui reaches for these with dlopen rather than linking them, so being
96 # in the cdylib's buildInputs is not enough — whatever starts frq has to
97 # put them on the loader path itself. Without libX11 here, vidya reports
98 # "X11 unavailable", falls back to Wayland, and winit refuses to build a
99 # second event loop after the failed first one.
100 #
101 # Out here rather than beside the package that first needed them: the
102 # dev shell starts frq too, on this tree's source rather than the store's
103 # copy of it, and a second copy of this list is a second chance for the
104 # two ways of running to disagree about what the window needs.
105 runtimeLibsFor = pkgs: with pkgs; [
106 libGL
107 libxkbcommon
108 wayland
109 xorg.libX11
110 xorg.libXcursor
111 xorg.libXi
112 xorg.libXrandr
113 vulkan-loader
114 ];
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago115 in
116 {
117 packages = forEachSystem (pkgs:
118 let
119 inherit (pkgs) lib;
120
Run this tree on a native half the builders made a32699e nandi 17d ago121 nixGL = nixGLFor pkgs;
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago122
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago123 # libvidya (the retained-tree ABI glimmer-vidya binds, on egui) and
124 # libjoltmoq (the AV media plane). One workspace, two cdylibs.
125 native = pkgs.rustPlatform.buildRustPackage {
126 pname = "jolt-native";
127 version = "0.1.0";
128 src = jolt-native;
129
130 cargoLock = {
131 lockFile = "${jolt-native}/Cargo.lock";
132 allowBuiltinFetchGit = true;
133 };
134
135 nativeBuildInputs = with pkgs; [
136 pkg-config
137 cmake
138 rustPlatform.bindgenHook
139 ];
140
141 buildInputs = with pkgs; [
142 alsa-lib
143 pipewire
144 openssl
145 libxkbcommon
146 wayland
147 libGL
148 ];
149
150 # Upstream's .cargo/config.toml drives the whole build through
151 # DotSlash: a pinned rustc, sccache, and zig as the C/C++ compiler
152 # and linker, each fetched from the network on first use. None of
153 # that survives a build sandbox, and none of it is needed when the
154 # toolchain comes from the store — so drop it and let stdenv's cc
155 # link (openh264 is C++, which stdenv covers too).
156 postPatch = ''
157 rm -f .cargo/config.toml
158 '';
159
160 # bindgen reads linux/videodev2.h directly; upstream points it at
161 # zig's bundled headers, which under Nix is just the kernel headers.
162 V4L2R_VIDEODEV2_H_PATH = "${pkgs.linuxHeaders}/include";
163
164 doCheck = false;
165
166 installPhase = ''
167 runHook preInstall
168 mkdir -p "$out/lib"
169 install -m644 target/*/release/*.so "$out/lib/"
170 runHook postInstall
171 '';
172 };
173
174 # 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 ago175 # A function, because there are two of them — upstream for the
176 # desktop, and the Bionic-addrinfo fork for the boot image the APK
177 # carries. Nothing else about the build differs.
178 joltFrom = src: pkgs.stdenv.mkDerivation {
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago179 pname = "jolt";
180 version = "dev";
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago181 inherit src;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago182
183 strictDeps = true;
184 nativeBuildInputs = with pkgs; [ chez makeWrapper pkg-config xxd ];
185 buildInputs = with pkgs; [ lz4 zlib ncurses openssl libuuid ];
186
187 JOLT_VERSION = "dev";
188 dontConfigure = true;
189
190 buildPhase = ''
191 runHook preBuild
192 scheme --script host/chez/build-jolt.ss release target/release/jolt
193 runHook postBuild
194 '';
195
196 installPhase = ''
197 runHook preInstall
198 mkdir -p "$out/bin"
199 install -m755 target/release/jolt "$out/bin/jolt"
200 runHook postInstall
201 '';
202
203 # jolt.deps shells out to git and unzip, and jolt.mvn-http dlopens
204 # OpenSSL through the JOLT_OPENSSL_LIBDIR seam.
Read the clock in the reader's zone when /etc/localtime is a file bffe879 nandi 18d ago205 #
206 # TZDIR so a zone *name* resolves wherever this runs: frq.clock
207 # hands one to tzset, and glibc then looks for the tzfile under
208 # /usr/share/zoneinfo unless told otherwise — which a NixOS host
209 # does not have. The store's own tzdata is there on both kinds of
210 # 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 ago211 postFixup = ''
212 wrapProgram "$out/bin/jolt" \
213 --prefix PATH : "${pkgs.lib.makeBinPath [ pkgs.git pkgs.unzip ]}" \
214 --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 ago215 --set-default TZDIR "${pkgs.tzdata}/share/zoneinfo" \
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago216 --set-default SSL_CERT_FILE "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
217 '';
218 };
219
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago220 joltRuntime = joltFrom jolt-src;
221 joltAndroid = joltFrom jolt-android-src;
222
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago223 # glimmer-vidya lives inside the jolt-native checkout, and its own
224 # deps.edn asks for glimmer by git — the top-level override below
225 # answers for both.
226 glimmerVidya = "${jolt-native}/jolt/glimmer-vidya";
227
Run this tree on a native half the builders made a32699e nandi 17d ago228 runtimeLibs = runtimeLibsFor pkgs;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago229
230 # The project as jolt sees it: source, deps.edn, nothing else.
231 frqSource = pkgs.runCommand "frq-source" { } ''
232 mkdir -p "$out"
233 cp -r ${self}/src ${self}/deps.edn "$out/"
234 '';
235
236 # Jolt resolves deps.edn from the working directory, so the launcher
237 # runs from the store copy. Its .jolt/cpcache write lands on a
238 # read-only directory and jolt treats that as a quiet cache miss, so
239 # the only cost is re-resolving the (already local) graph per start.
240 frqScript = pkgs.writeShellScript "frq" ''
241 export LD_LIBRARY_PATH="${native}/lib:${lib.makeLibraryPath runtimeLibs}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
242 cd ${frqSource}
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago243
244 # On NixOS the store's Mesa is the system's and the window opens.
245 # Anywhere else the real driver is the host's, so defer to nixGL
246 # it prepends the host driver, which has to win over ours.
247 runner=""
248 [ -e /run/current-system ] || runner="${nixGL}/bin/nixGLIntel"
249
250 exec ''${runner} ${joltRuntime}/bin/jolt \
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago251 -Sdeps '{:deps {jolt-lang/glimmer {:local/root "${glimmer}"} nandi/glimmer-vidya {:local/root "${glimmerVidya}"}}}' \
252 -M:frq "$@"
253 '';
254
255 frq = pkgs.runCommand "frq-0.1.0"
256 {
257 meta = {
258 description = "A freeq client in jolt";
259 mainProgram = "frq";
260 platforms = systems;
261 };
262 }
263 ''
264 mkdir -p "$out/bin"
265 ln -s ${frqScript} "$out/bin/frq"
266 '';
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago267 # --- Android ------------------------------------------------------
268 # The SDK and the NDK are Google's, which means unfree and a licence
269 # to accept — so this is its own import of nixpkgs rather than the
270 # `legacyPackages` everything above uses. Confined to the Android
271 # outputs: `nix build` of frq itself never evaluates it.
272 #
273 # The NDK here is r29, which is the version scripts/android-ndk.dotslash
274 # pins and the one the pinned libvidya was built with.
275 androidPkgs = import nixpkgs {
276 inherit (pkgs.stdenv.hostPlatform) system;
277 config = {
278 allowUnfree = true;
279 android_sdk.accept_license = true;
280 };
281 };
282
283 androidComposition = androidPkgs.androidenv.composeAndroidPackages {
284 buildToolsVersions = [ "36.0.0" ];
285 platformVersions = [ "36" ];
286 includeNDK = true;
287 };
288
289 android = import ./nix/android.nix {
290 inherit pkgs self chez-src jolt-native glimmer joltAndroid;
291 inherit (pkgs) lib;
292 androidSdk = androidComposition.androidsdk;
293 ndk = androidComposition.ndk-bundle;
294 };
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago295 in
296 {
297 inherit native frq;
298 jolt = joltRuntime;
299 default = frq;
Bundle the closure into one file the AppImage runtime mounts f82b93f nandi 18d ago300
301 # frq and everything it loads, squashed into one runnable file for
302 # hosts without Nix. The whole closure rides along — Mesa included,
303 # which is not waste: off NixOS the launcher goes through nixGL, and
304 # nixGL needs a store Mesa to put the host's driver in front of.
305 appimage =
306 nix-appimage.bundlers.${pkgs.stdenv.hostPlatform.system}.default frq;
Build the APK from the flake, and from nothing 6aa2a6b nandi 18d ago307 }
308 # An APK is built by a linux-x86_64 NDK and a linux-x86_64 jolt, and
309 # Google ships no other; on aarch64 the Android outputs are simply
310 # absent rather than present and broken.
311 // lib.optionalAttrs (pkgs.stdenv.hostPlatform.system == "x86_64-linux") {
312 inherit (android) apk chezAndroid joltBoot libjoltapp;
313 apk-unsigned = android.apk-unsigned;
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago314 });
315
Run this tree on a native half the builders made a32699e nandi 17d ago316 # Where `just run` runs, and — because entering it realises what it
317 # names — what builds the half of frq that is not this working tree.
318 #
319 # The two halves, and the split is the whole point of the shell. The frq
320 # source is the files on disk, uncommitted edits and all. Everything
321 # under it — jolt, glimmer, glimmer-vidya, both native objects — is the
322 # flake's, at the revs flake.lock names, so a run says what it ran
323 # against and both halves of glimmer-vidya move together. That is the
324 # drift the `jolt-native` input's comment is about, and a pin frq can
325 # answer for is worth more here than the convenience of a checkout.
326 #
327 # `native` is the cargo build of that input rather than jolt-native's own
328 # buck2 graph, which is a compromise and not a free one: buck2 is what
329 # its CI runs and what makes its releases, and its cpal has the pipewire
330 # feature this one does not, so device *names* in a call come out as ALSA
331 # PCMs. What it buys is a derivation — one thing nixbuild.net can be
332 # handed. The buck2 build fetches its rustc, zig and every third-party
333 # crate as it goes and writes buck-out into the tree it builds; a sandbox
334 # with no network and a read-only store is the one place it cannot run,
335 # so on a remote builder it is not a slower option but no option at all.
336 #
337 # Nothing here says "nixbuild", though: it is a plain derivation, and
338 # where it gets built is the machine's business. scripts/run.bb asks for
339 # the shell with --max-jobs 0, which is what sends it to the `builders`
340 # entry rather than compiling egui on a laptop.
341 devShells = forEachSystem (pkgs:
342 let
343 inherit (pkgs) lib;
344 inherit (self.packages.${pkgs.stdenv.hostPlatform.system}) jolt native;
345 in
346 {
347 default = pkgs.mkShellNoCC {
348 name = "frq";
349
350 # jolt, because the runtime frq is run by should be the flake's
351 # too. nixGL for the same reason the launcher reaches for it — see
352 # frqScript.
353 packages = [ jolt (nixGLFor pkgs) ];
354
355 # Read by scripts/run.bb rather than baked into a wrapper: the frq
356 # source `just run` runs is the working tree, so the launcher has
357 # to be a script in that tree and the shell has to hand it its
358 # answers. Naming these is also what makes the shell build them.
359 JOLT_NATIVE_LIB = "${native}/lib";
360 GLIMMER_SRC = glimmer;
361 GLIMMER_VIDYA_SRC = "${jolt-native}/jolt/glimmer-vidya";
362 FRQ_LIB_PATH = lib.makeLibraryPath (runtimeLibsFor pkgs);
363 NIXGL = "${nixGLFor pkgs}/bin/nixGLIntel";
364 };
365 });
366
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago367 apps = forEachSystem (pkgs: {
368 default = {
369 type = "app";
370 program = "${self.packages.${pkgs.stdenv.hostPlatform.system}.frq}/bin/frq";
371 };
372 });
373 };
374}