Build the client with Nix, not just a shell for it
frq is Jolt source, so a package is three things rather than one: the
runtime that reads it, the two Rust shared objects it loads, and this
tree with its deps resolved to store paths.
The parts that did not come for free:
- jolt's own flake declares `self.submodules`, which Nix rejects over
the github scheme, so its source comes in as git+https and the
derivation lives here.
- jolt-native's .cargo/config.toml runs the build through DotSlash — a
pinned rustc, sccache, zig as compiler and linker — all fetched from
the network on first use, which a sandbox has none of.
- jolt resolves deps.edn by shelling out to git at startup, so glimmer
and glimmer-vidya are Nix inputs handed back as :local/root.
- the launcher is a written script, not makeWrapper: --add-flags pastes
in unquoted, which splits the -Sdeps map across five arguments and
leaves jolt reading one of them to EOF.
- egui dlopens libX11 and libGL, so the launcher puts them on the
loader path itself.
Off NixOS the GL driver is the host's and the loader will not find it;
the flake header says to launch through nixGL there.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>ca3afbe parent: f3d5db6 modified
.gitignore +4 -0 | @@ -5,3 +5,7 @@ | ||
| 5 | 5 | /.jolt/ |
| 6 | 6 | /.lsp/ |
| 7 | 7 | /.clj-kondo/ |
| 8 | + | |
| 9 | +# Nix build symlinks. | |
| 10 | +/result | |
| 11 | +/result-* | |
| @@ -5,3 +5,7 @@ | |||
| 5 | /.jolt/ | 5 | /.jolt/ |
| 6 | /.lsp/ | 6 | /.lsp/ |
| 7 | /.clj-kondo/ | 7 | /.clj-kondo/ |
| 8 | + | ||
| 9 | +# Nix build symlinks. | ||
| 10 | +/result | ||
| 11 | +/result-* | ||
added
flake.lock +81 -0 | new file mode 100644 | ||
| @@ -0,0 +1,81 @@ | ||
| 1 | +{ | |
| 2 | + "nodes": { | |
| 3 | + "glimmer": { | |
| 4 | + "flake": false, | |
| 5 | + "locked": { | |
| 6 | + "lastModified": 1788135429, | |
| 7 | + "narHash": "sha256-20W4kmcL05O7V7EQKXuH+kVBpBtQZhhj5yTm9QqxQss=", | |
| 8 | + "ref": "refs/heads/main", | |
| 9 | + "rev": "399df371c790d690fb6e4560c3d4d7f838502857", | |
| 10 | + "revCount": 32, | |
| 11 | + "type": "git", | |
| 12 | + "url": "https://gitlab.com/nandithebull/glimmer" | |
| 13 | + }, | |
| 14 | + "original": { | |
| 15 | + "rev": "399df371c790d690fb6e4560c3d4d7f838502857", | |
| 16 | + "type": "git", | |
| 17 | + "url": "https://gitlab.com/nandithebull/glimmer" | |
| 18 | + } | |
| 19 | + }, | |
| 20 | + "jolt-native": { | |
| 21 | + "flake": false, | |
| 22 | + "locked": { | |
| 23 | + "lastModified": 1788142815, | |
| 24 | + "narHash": "sha256-ehaIvgeA7ce9m7T9AxyPTHpnYVrG2lQRH3RkkF612GY=", | |
| 25 | + "ref": "refs/heads/main", | |
| 26 | + "rev": "70072e8e48ad396caeab6f6bd18b999cf449ec8f", | |
| 27 | + "revCount": 13, | |
| 28 | + "type": "git", | |
| 29 | + "url": "https://gitlab.com/nandithebull/jolt-native" | |
| 30 | + }, | |
| 31 | + "original": { | |
| 32 | + "type": "git", | |
| 33 | + "url": "https://gitlab.com/nandithebull/jolt-native" | |
| 34 | + } | |
| 35 | + }, | |
| 36 | + "jolt-src": { | |
| 37 | + "flake": false, | |
| 38 | + "locked": { | |
| 39 | + "lastModified": 1788135812, | |
| 40 | + "narHash": "sha256-DvVmjNN09mfxp67Y9zOyXNbAIYR9h2gpuyTbdj777a8=", | |
| 41 | + "ref": "refs/heads/main", | |
| 42 | + "rev": "6f1d436f6e4cb1e2f878c5fc90c39a48a8bf4310", | |
| 43 | + "revCount": 2803, | |
| 44 | + "submodules": true, | |
| 45 | + "type": "git", | |
| 46 | + "url": "https://github.com/jolt-lang/jolt" | |
| 47 | + }, | |
| 48 | + "original": { | |
| 49 | + "submodules": true, | |
| 50 | + "type": "git", | |
| 51 | + "url": "https://github.com/jolt-lang/jolt" | |
| 52 | + } | |
| 53 | + }, | |
| 54 | + "nixpkgs": { | |
| 55 | + "locked": { | |
| 56 | + "lastModified": 1788039129, | |
| 57 | + "narHash": "sha256-pa4Q0qErvCvzCaaUph7Sm37RhR4xvPrYI8Lgz6k85+A=", | |
| 58 | + "owner": "NixOS", | |
| 59 | + "repo": "nixpkgs", | |
| 60 | + "rev": "d2f67949798825fe853f7c5d0492b8bf016d3f88", | |
| 61 | + "type": "github" | |
| 62 | + }, | |
| 63 | + "original": { | |
| 64 | + "owner": "NixOS", | |
| 65 | + "ref": "nixos-unstable", | |
| 66 | + "repo": "nixpkgs", | |
| 67 | + "type": "github" | |
| 68 | + } | |
| 69 | + }, | |
| 70 | + "root": { | |
| 71 | + "inputs": { | |
| 72 | + "glimmer": "glimmer", | |
| 73 | + "jolt-native": "jolt-native", | |
| 74 | + "jolt-src": "jolt-src", | |
| 75 | + "nixpkgs": "nixpkgs" | |
| 76 | + } | |
| 77 | + } | |
| 78 | + }, | |
| 79 | + "root": "root", | |
| 80 | + "version": 7 | |
| 81 | +} | |
| new file mode 100644 | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | +{ | ||
| 2 | + "nodes": { | ||
| 3 | + "glimmer": { | ||
| 4 | + "flake": false, | ||
| 5 | + "locked": { | ||
| 6 | + "lastModified": 1788135429, | ||
| 7 | + "narHash": "sha256-20W4kmcL05O7V7EQKXuH+kVBpBtQZhhj5yTm9QqxQss=", | ||
| 8 | + "ref": "refs/heads/main", | ||
| 9 | + "rev": "399df371c790d690fb6e4560c3d4d7f838502857", | ||
| 10 | + "revCount": 32, | ||
| 11 | + "type": "git", | ||
| 12 | + "url": "https://gitlab.com/nandithebull/glimmer" | ||
| 13 | + }, | ||
| 14 | + "original": { | ||
| 15 | + "rev": "399df371c790d690fb6e4560c3d4d7f838502857", | ||
| 16 | + "type": "git", | ||
| 17 | + "url": "https://gitlab.com/nandithebull/glimmer" | ||
| 18 | + } | ||
| 19 | + }, | ||
| 20 | + "jolt-native": { | ||
| 21 | + "flake": false, | ||
| 22 | + "locked": { | ||
| 23 | + "lastModified": 1788142815, | ||
| 24 | + "narHash": "sha256-ehaIvgeA7ce9m7T9AxyPTHpnYVrG2lQRH3RkkF612GY=", | ||
| 25 | + "ref": "refs/heads/main", | ||
| 26 | + "rev": "70072e8e48ad396caeab6f6bd18b999cf449ec8f", | ||
| 27 | + "revCount": 13, | ||
| 28 | + "type": "git", | ||
| 29 | + "url": "https://gitlab.com/nandithebull/jolt-native" | ||
| 30 | + }, | ||
| 31 | + "original": { | ||
| 32 | + "type": "git", | ||
| 33 | + "url": "https://gitlab.com/nandithebull/jolt-native" | ||
| 34 | + } | ||
| 35 | + }, | ||
| 36 | + "jolt-src": { | ||
| 37 | + "flake": false, | ||
| 38 | + "locked": { | ||
| 39 | + "lastModified": 1788135812, | ||
| 40 | + "narHash": "sha256-DvVmjNN09mfxp67Y9zOyXNbAIYR9h2gpuyTbdj777a8=", | ||
| 41 | + "ref": "refs/heads/main", | ||
| 42 | + "rev": "6f1d436f6e4cb1e2f878c5fc90c39a48a8bf4310", | ||
| 43 | + "revCount": 2803, | ||
| 44 | + "submodules": true, | ||
| 45 | + "type": "git", | ||
| 46 | + "url": "https://github.com/jolt-lang/jolt" | ||
| 47 | + }, | ||
| 48 | + "original": { | ||
| 49 | + "submodules": true, | ||
| 50 | + "type": "git", | ||
| 51 | + "url": "https://github.com/jolt-lang/jolt" | ||
| 52 | + } | ||
| 53 | + }, | ||
| 54 | + "nixpkgs": { | ||
| 55 | + "locked": { | ||
| 56 | + "lastModified": 1788039129, | ||
| 57 | + "narHash": "sha256-pa4Q0qErvCvzCaaUph7Sm37RhR4xvPrYI8Lgz6k85+A=", | ||
| 58 | + "owner": "NixOS", | ||
| 59 | + "repo": "nixpkgs", | ||
| 60 | + "rev": "d2f67949798825fe853f7c5d0492b8bf016d3f88", | ||
| 61 | + "type": "github" | ||
| 62 | + }, | ||
| 63 | + "original": { | ||
| 64 | + "owner": "NixOS", | ||
| 65 | + "ref": "nixos-unstable", | ||
| 66 | + "repo": "nixpkgs", | ||
| 67 | + "type": "github" | ||
| 68 | + } | ||
| 69 | + }, | ||
| 70 | + "root": { | ||
| 71 | + "inputs": { | ||
| 72 | + "glimmer": "glimmer", | ||
| 73 | + "jolt-native": "jolt-native", | ||
| 74 | + "jolt-src": "jolt-src", | ||
| 75 | + "nixpkgs": "nixpkgs" | ||
| 76 | + } | ||
| 77 | + } | ||
| 78 | + }, | ||
| 79 | + "root": "root", | ||
| 80 | + "version": 7 | ||
| 81 | +} | ||
added
flake.nix +206 -0 | new file mode 100644 | ||
| @@ -0,0 +1,206 @@ | ||
| 1 | +{ | |
| 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 | |
| 16 | + # name a valid config"). Launch through nixGL there: | |
| 17 | + # | |
| 18 | + # nix run --impure "git+https://github.com/nix-community/nixGL#nixGLIntel" -- ./result/bin/frq | |
| 19 | + description = "frq — a freeq client in jolt"; | |
| 20 | + | |
| 21 | + inputs = { | |
| 22 | + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; | |
| 23 | + # Jolt's own flake declares `self.submodules`, which this Nix rejects when | |
| 24 | + # the flake is fetched through the github scheme — so take the source and | |
| 25 | + # build it here. `vendor/` is a submodule and the build needs it. | |
| 26 | + jolt-src = { | |
| 27 | + url = "git+https://github.com/jolt-lang/jolt?submodules=1"; | |
| 28 | + flake = false; | |
| 29 | + }; | |
| 30 | + | |
| 31 | + jolt-native = { | |
| 32 | + url = "git+https://gitlab.com/nandithebull/jolt-native"; | |
| 33 | + flake = false; | |
| 34 | + }; | |
| 35 | + | |
| 36 | + # The sha deps.edn pins, on the fork with the reconciler fixes. | |
| 37 | + glimmer = { | |
| 38 | + url = "git+https://gitlab.com/nandithebull/glimmer?rev=399df371c790d690fb6e4560c3d4d7f838502857"; | |
| 39 | + flake = false; | |
| 40 | + }; | |
| 41 | + }; | |
| 42 | + | |
| 43 | + outputs = { self, nixpkgs, jolt-src, jolt-native, glimmer }: | |
| 44 | + let | |
| 45 | + systems = [ "x86_64-linux" "aarch64-linux" ]; | |
| 46 | + forEachSystem = f: | |
| 47 | + nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system}); | |
| 48 | + in | |
| 49 | + { | |
| 50 | + packages = forEachSystem (pkgs: | |
| 51 | + let | |
| 52 | + inherit (pkgs) lib; | |
| 53 | + | |
| 54 | + # libvidya (the retained-tree ABI glimmer-vidya binds, on egui) and | |
| 55 | + # libjoltmoq (the AV media plane). One workspace, two cdylibs. | |
| 56 | + native = pkgs.rustPlatform.buildRustPackage { | |
| 57 | + pname = "jolt-native"; | |
| 58 | + version = "0.1.0"; | |
| 59 | + src = jolt-native; | |
| 60 | + | |
| 61 | + cargoLock = { | |
| 62 | + lockFile = "${jolt-native}/Cargo.lock"; | |
| 63 | + allowBuiltinFetchGit = true; | |
| 64 | + }; | |
| 65 | + | |
| 66 | + nativeBuildInputs = with pkgs; [ | |
| 67 | + pkg-config | |
| 68 | + cmake | |
| 69 | + rustPlatform.bindgenHook | |
| 70 | + ]; | |
| 71 | + | |
| 72 | + buildInputs = with pkgs; [ | |
| 73 | + alsa-lib | |
| 74 | + pipewire | |
| 75 | + openssl | |
| 76 | + libxkbcommon | |
| 77 | + wayland | |
| 78 | + libGL | |
| 79 | + ]; | |
| 80 | + | |
| 81 | + # Upstream's .cargo/config.toml drives the whole build through | |
| 82 | + # DotSlash: a pinned rustc, sccache, and zig as the C/C++ compiler | |
| 83 | + # and linker, each fetched from the network on first use. None of | |
| 84 | + # that survives a build sandbox, and none of it is needed when the | |
| 85 | + # toolchain comes from the store — so drop it and let stdenv's cc | |
| 86 | + # link (openh264 is C++, which stdenv covers too). | |
| 87 | + postPatch = '' | |
| 88 | + rm -f .cargo/config.toml | |
| 89 | + ''; | |
| 90 | + | |
| 91 | + # bindgen reads linux/videodev2.h directly; upstream points it at | |
| 92 | + # zig's bundled headers, which under Nix is just the kernel headers. | |
| 93 | + V4L2R_VIDEODEV2_H_PATH = "${pkgs.linuxHeaders}/include"; | |
| 94 | + | |
| 95 | + doCheck = false; | |
| 96 | + | |
| 97 | + installPhase = '' | |
| 98 | + runHook preInstall | |
| 99 | + mkdir -p "$out/lib" | |
| 100 | + install -m644 target/*/release/*.so "$out/lib/" | |
| 101 | + runHook postInstall | |
| 102 | + ''; | |
| 103 | + }; | |
| 104 | + | |
| 105 | + # Jolt itself: Clojure on Chez, built the way its own flake builds it. | |
| 106 | + joltRuntime = pkgs.stdenv.mkDerivation { | |
| 107 | + pname = "jolt"; | |
| 108 | + version = "dev"; | |
| 109 | + src = jolt-src; | |
| 110 | + | |
| 111 | + strictDeps = true; | |
| 112 | + nativeBuildInputs = with pkgs; [ chez makeWrapper pkg-config xxd ]; | |
| 113 | + buildInputs = with pkgs; [ lz4 zlib ncurses openssl libuuid ]; | |
| 114 | + | |
| 115 | + JOLT_VERSION = "dev"; | |
| 116 | + dontConfigure = true; | |
| 117 | + | |
| 118 | + buildPhase = '' | |
| 119 | + runHook preBuild | |
| 120 | + scheme --script host/chez/build-jolt.ss release target/release/jolt | |
| 121 | + runHook postBuild | |
| 122 | + ''; | |
| 123 | + | |
| 124 | + installPhase = '' | |
| 125 | + runHook preInstall | |
| 126 | + mkdir -p "$out/bin" | |
| 127 | + install -m755 target/release/jolt "$out/bin/jolt" | |
| 128 | + runHook postInstall | |
| 129 | + ''; | |
| 130 | + | |
| 131 | + # jolt.deps shells out to git and unzip, and jolt.mvn-http dlopens | |
| 132 | + # OpenSSL through the JOLT_OPENSSL_LIBDIR seam. | |
| 133 | + postFixup = '' | |
| 134 | + wrapProgram "$out/bin/jolt" \ | |
| 135 | + --prefix PATH : "${pkgs.lib.makeBinPath [ pkgs.git pkgs.unzip ]}" \ | |
| 136 | + --set-default JOLT_OPENSSL_LIBDIR "${pkgs.lib.makeLibraryPath [ pkgs.openssl ]}" \ | |
| 137 | + --set-default SSL_CERT_FILE "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" | |
| 138 | + ''; | |
| 139 | + }; | |
| 140 | + | |
| 141 | + # glimmer-vidya lives inside the jolt-native checkout, and its own | |
| 142 | + # deps.edn asks for glimmer by git — the top-level override below | |
| 143 | + # answers for both. | |
| 144 | + glimmerVidya = "${jolt-native}/jolt/glimmer-vidya"; | |
| 145 | + | |
| 146 | + # egui reaches for these with dlopen rather than linking them, so | |
| 147 | + # being in the cdylib's buildInputs is not enough — the launcher has | |
| 148 | + # to put them on the loader path itself. Without libX11 here, vidya | |
| 149 | + # reports "X11 unavailable", falls back to Wayland, and winit refuses | |
| 150 | + # to build a second event loop after the failed first one. | |
| 151 | + runtimeLibs = with pkgs; [ | |
| 152 | + libGL | |
| 153 | + libxkbcommon | |
| 154 | + wayland | |
| 155 | + xorg.libX11 | |
| 156 | + xorg.libXcursor | |
| 157 | + xorg.libXi | |
| 158 | + xorg.libXrandr | |
| 159 | + vulkan-loader | |
| 160 | + ]; | |
| 161 | + | |
| 162 | + # The project as jolt sees it: source, deps.edn, nothing else. | |
| 163 | + frqSource = pkgs.runCommand "frq-source" { } '' | |
| 164 | + mkdir -p "$out" | |
| 165 | + cp -r ${self}/src ${self}/deps.edn "$out/" | |
| 166 | + ''; | |
| 167 | + | |
| 168 | + # Jolt resolves deps.edn from the working directory, so the launcher | |
| 169 | + # runs from the store copy. Its .jolt/cpcache write lands on a | |
| 170 | + # read-only directory and jolt treats that as a quiet cache miss, so | |
| 171 | + # the only cost is re-resolving the (already local) graph per start. | |
| 172 | + frqScript = pkgs.writeShellScript "frq" '' | |
| 173 | + export LD_LIBRARY_PATH="${native}/lib:${lib.makeLibraryPath runtimeLibs}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" | |
| 174 | + cd ${frqSource} | |
| 175 | + exec ${joltRuntime}/bin/jolt \ | |
| 176 | + -Sdeps '{:deps {jolt-lang/glimmer {:local/root "${glimmer}"} nandi/glimmer-vidya {:local/root "${glimmerVidya}"}}}' \ | |
| 177 | + -M:frq "$@" | |
| 178 | + ''; | |
| 179 | + | |
| 180 | + frq = pkgs.runCommand "frq-0.1.0" | |
| 181 | + { | |
| 182 | + meta = { | |
| 183 | + description = "A freeq client in jolt"; | |
| 184 | + mainProgram = "frq"; | |
| 185 | + platforms = systems; | |
| 186 | + }; | |
| 187 | + } | |
| 188 | + '' | |
| 189 | + mkdir -p "$out/bin" | |
| 190 | + ln -s ${frqScript} "$out/bin/frq" | |
| 191 | + ''; | |
| 192 | + in | |
| 193 | + { | |
| 194 | + inherit native frq; | |
| 195 | + jolt = joltRuntime; | |
| 196 | + default = frq; | |
| 197 | + }); | |
| 198 | + | |
| 199 | + apps = forEachSystem (pkgs: { | |
| 200 | + default = { | |
| 201 | + type = "app"; | |
| 202 | + program = "${self.packages.${pkgs.stdenv.hostPlatform.system}.frq}/bin/frq"; | |
| 203 | + }; | |
| 204 | + }); | |
| 205 | + }; | |
| 206 | +} | |
| new file mode 100644 | |||
| @@ -0,0 +1,206 @@ | |||
| 1 | +{ | ||
| 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 | ||
| 16 | + # name a valid config"). Launch through nixGL there: | ||
| 17 | + # | ||
| 18 | + # nix run --impure "git+https://github.com/nix-community/nixGL#nixGLIntel" -- ./result/bin/frq | ||
| 19 | + description = "frq — a freeq client in jolt"; | ||
| 20 | + | ||
| 21 | + inputs = { | ||
| 22 | + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; | ||
| 23 | + # Jolt's own flake declares `self.submodules`, which this Nix rejects when | ||
| 24 | + # the flake is fetched through the github scheme — so take the source and | ||
| 25 | + # build it here. `vendor/` is a submodule and the build needs it. | ||
| 26 | + jolt-src = { | ||
| 27 | + url = "git+https://github.com/jolt-lang/jolt?submodules=1"; | ||
| 28 | + flake = false; | ||
| 29 | + }; | ||
| 30 | + | ||
| 31 | + jolt-native = { | ||
| 32 | + url = "git+https://gitlab.com/nandithebull/jolt-native"; | ||
| 33 | + flake = false; | ||
| 34 | + }; | ||
| 35 | + | ||
| 36 | + # The sha deps.edn pins, on the fork with the reconciler fixes. | ||
| 37 | + glimmer = { | ||
| 38 | + url = "git+https://gitlab.com/nandithebull/glimmer?rev=399df371c790d690fb6e4560c3d4d7f838502857"; | ||
| 39 | + flake = false; | ||
| 40 | + }; | ||
| 41 | + }; | ||
| 42 | + | ||
| 43 | + outputs = { self, nixpkgs, jolt-src, jolt-native, glimmer }: | ||
| 44 | + let | ||
| 45 | + systems = [ "x86_64-linux" "aarch64-linux" ]; | ||
| 46 | + forEachSystem = f: | ||
| 47 | + nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system}); | ||
| 48 | + in | ||
| 49 | + { | ||
| 50 | + packages = forEachSystem (pkgs: | ||
| 51 | + let | ||
| 52 | + inherit (pkgs) lib; | ||
| 53 | + | ||
| 54 | + # libvidya (the retained-tree ABI glimmer-vidya binds, on egui) and | ||
| 55 | + # libjoltmoq (the AV media plane). One workspace, two cdylibs. | ||
| 56 | + native = pkgs.rustPlatform.buildRustPackage { | ||
| 57 | + pname = "jolt-native"; | ||
| 58 | + version = "0.1.0"; | ||
| 59 | + src = jolt-native; | ||
| 60 | + | ||
| 61 | + cargoLock = { | ||
| 62 | + lockFile = "${jolt-native}/Cargo.lock"; | ||
| 63 | + allowBuiltinFetchGit = true; | ||
| 64 | + }; | ||
| 65 | + | ||
| 66 | + nativeBuildInputs = with pkgs; [ | ||
| 67 | + pkg-config | ||
| 68 | + cmake | ||
| 69 | + rustPlatform.bindgenHook | ||
| 70 | + ]; | ||
| 71 | + | ||
| 72 | + buildInputs = with pkgs; [ | ||
| 73 | + alsa-lib | ||
| 74 | + pipewire | ||
| 75 | + openssl | ||
| 76 | + libxkbcommon | ||
| 77 | + wayland | ||
| 78 | + libGL | ||
| 79 | + ]; | ||
| 80 | + | ||
| 81 | + # Upstream's .cargo/config.toml drives the whole build through | ||
| 82 | + # DotSlash: a pinned rustc, sccache, and zig as the C/C++ compiler | ||
| 83 | + # and linker, each fetched from the network on first use. None of | ||
| 84 | + # that survives a build sandbox, and none of it is needed when the | ||
| 85 | + # toolchain comes from the store — so drop it and let stdenv's cc | ||
| 86 | + # link (openh264 is C++, which stdenv covers too). | ||
| 87 | + postPatch = '' | ||
| 88 | + rm -f .cargo/config.toml | ||
| 89 | + ''; | ||
| 90 | + | ||
| 91 | + # bindgen reads linux/videodev2.h directly; upstream points it at | ||
| 92 | + # zig's bundled headers, which under Nix is just the kernel headers. | ||
| 93 | + V4L2R_VIDEODEV2_H_PATH = "${pkgs.linuxHeaders}/include"; | ||
| 94 | + | ||
| 95 | + doCheck = false; | ||
| 96 | + | ||
| 97 | + installPhase = '' | ||
| 98 | + runHook preInstall | ||
| 99 | + mkdir -p "$out/lib" | ||
| 100 | + install -m644 target/*/release/*.so "$out/lib/" | ||
| 101 | + runHook postInstall | ||
| 102 | + ''; | ||
| 103 | + }; | ||
| 104 | + | ||
| 105 | + # Jolt itself: Clojure on Chez, built the way its own flake builds it. | ||
| 106 | + joltRuntime = pkgs.stdenv.mkDerivation { | ||
| 107 | + pname = "jolt"; | ||
| 108 | + version = "dev"; | ||
| 109 | + src = jolt-src; | ||
| 110 | + | ||
| 111 | + strictDeps = true; | ||
| 112 | + nativeBuildInputs = with pkgs; [ chez makeWrapper pkg-config xxd ]; | ||
| 113 | + buildInputs = with pkgs; [ lz4 zlib ncurses openssl libuuid ]; | ||
| 114 | + | ||
| 115 | + JOLT_VERSION = "dev"; | ||
| 116 | + dontConfigure = true; | ||
| 117 | + | ||
| 118 | + buildPhase = '' | ||
| 119 | + runHook preBuild | ||
| 120 | + scheme --script host/chez/build-jolt.ss release target/release/jolt | ||
| 121 | + runHook postBuild | ||
| 122 | + ''; | ||
| 123 | + | ||
| 124 | + installPhase = '' | ||
| 125 | + runHook preInstall | ||
| 126 | + mkdir -p "$out/bin" | ||
| 127 | + install -m755 target/release/jolt "$out/bin/jolt" | ||
| 128 | + runHook postInstall | ||
| 129 | + ''; | ||
| 130 | + | ||
| 131 | + # jolt.deps shells out to git and unzip, and jolt.mvn-http dlopens | ||
| 132 | + # OpenSSL through the JOLT_OPENSSL_LIBDIR seam. | ||
| 133 | + postFixup = '' | ||
| 134 | + wrapProgram "$out/bin/jolt" \ | ||
| 135 | + --prefix PATH : "${pkgs.lib.makeBinPath [ pkgs.git pkgs.unzip ]}" \ | ||
| 136 | + --set-default JOLT_OPENSSL_LIBDIR "${pkgs.lib.makeLibraryPath [ pkgs.openssl ]}" \ | ||
| 137 | + --set-default SSL_CERT_FILE "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" | ||
| 138 | + ''; | ||
| 139 | + }; | ||
| 140 | + | ||
| 141 | + # glimmer-vidya lives inside the jolt-native checkout, and its own | ||
| 142 | + # deps.edn asks for glimmer by git — the top-level override below | ||
| 143 | + # answers for both. | ||
| 144 | + glimmerVidya = "${jolt-native}/jolt/glimmer-vidya"; | ||
| 145 | + | ||
| 146 | + # egui reaches for these with dlopen rather than linking them, so | ||
| 147 | + # being in the cdylib's buildInputs is not enough — the launcher has | ||
| 148 | + # to put them on the loader path itself. Without libX11 here, vidya | ||
| 149 | + # reports "X11 unavailable", falls back to Wayland, and winit refuses | ||
| 150 | + # to build a second event loop after the failed first one. | ||
| 151 | + runtimeLibs = with pkgs; [ | ||
| 152 | + libGL | ||
| 153 | + libxkbcommon | ||
| 154 | + wayland | ||
| 155 | + xorg.libX11 | ||
| 156 | + xorg.libXcursor | ||
| 157 | + xorg.libXi | ||
| 158 | + xorg.libXrandr | ||
| 159 | + vulkan-loader | ||
| 160 | + ]; | ||
| 161 | + | ||
| 162 | + # The project as jolt sees it: source, deps.edn, nothing else. | ||
| 163 | + frqSource = pkgs.runCommand "frq-source" { } '' | ||
| 164 | + mkdir -p "$out" | ||
| 165 | + cp -r ${self}/src ${self}/deps.edn "$out/" | ||
| 166 | + ''; | ||
| 167 | + | ||
| 168 | + # Jolt resolves deps.edn from the working directory, so the launcher | ||
| 169 | + # runs from the store copy. Its .jolt/cpcache write lands on a | ||
| 170 | + # read-only directory and jolt treats that as a quiet cache miss, so | ||
| 171 | + # the only cost is re-resolving the (already local) graph per start. | ||
| 172 | + frqScript = pkgs.writeShellScript "frq" '' | ||
| 173 | + export LD_LIBRARY_PATH="${native}/lib:${lib.makeLibraryPath runtimeLibs}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" | ||
| 174 | + cd ${frqSource} | ||
| 175 | + exec ${joltRuntime}/bin/jolt \ | ||
| 176 | + -Sdeps '{:deps {jolt-lang/glimmer {:local/root "${glimmer}"} nandi/glimmer-vidya {:local/root "${glimmerVidya}"}}}' \ | ||
| 177 | + -M:frq "$@" | ||
| 178 | + ''; | ||
| 179 | + | ||
| 180 | + frq = pkgs.runCommand "frq-0.1.0" | ||
| 181 | + { | ||
| 182 | + meta = { | ||
| 183 | + description = "A freeq client in jolt"; | ||
| 184 | + mainProgram = "frq"; | ||
| 185 | + platforms = systems; | ||
| 186 | + }; | ||
| 187 | + } | ||
| 188 | + '' | ||
| 189 | + mkdir -p "$out/bin" | ||
| 190 | + ln -s ${frqScript} "$out/bin/frq" | ||
| 191 | + ''; | ||
| 192 | + in | ||
| 193 | + { | ||
| 194 | + inherit native frq; | ||
| 195 | + jolt = joltRuntime; | ||
| 196 | + default = frq; | ||
| 197 | + }); | ||
| 198 | + | ||
| 199 | + apps = forEachSystem (pkgs: { | ||
| 200 | + default = { | ||
| 201 | + type = "app"; | ||
| 202 | + program = "${self.packages.${pkgs.stdenv.hostPlatform.system}.frq}/bin/frq"; | ||
| 203 | + }; | ||
| 204 | + }); | ||
| 205 | + }; | ||
| 206 | +} | ||