nandi/frqpublic Fork 0
58e6c977cefe22fd58b6953f781f182058e11fdc
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 · 642 lines · 33.3 KBNix Blame HistoryRaw
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago1{
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago2 # frq is a Flutter app whose source is ClojureDart, so what this flake
3 # provides is toolchains rather than a built program: the `flutter` shell
4 # that `just apk` compiles in, the `flutter-desktop` shell for the Linux
5 # target, and the Android SDK the first of those copies somewhere writable.
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago6 #
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago7 # nix develop .#flutter-desktop --command just flutter-desktop run
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago8 #
9 # On a machine that is not NixOS the GL driver is the host's and the loader
10 # will not find it, so the window never opens ("GL display: argument does not
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago11 # name a valid config"). The recipes handle that themselves: off NixOS they
12 # hand the process to nixGL, which puts the host's driver ahead of the
13 # store's. A distrobox/container Arch is the same case as a bare one.
14 description = "frq a freeq client in Flutter";
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago15
16 inputs = {
17 nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
Find the host's GL driver when the store's is not the system's 633faad nandi 19d ago18
19 # Only ever used off NixOS, to put the host GL driver on the loader path.
20 nixgl = {
21 url = "github:nix-community/nixGL";
22 inputs.nixpkgs.follows = "nixpkgs";
23 };
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago24 };
25
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago26 outputs = { self, nixpkgs, nixgl }:
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago27 let
28 systems = [ "x86_64-linux" "aarch64-linux" ];
29 forEachSystem = f:
30 nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
Run this tree on a native half the builders made a32699e nandi 17d ago31
32 # Mesa, despite the name: it covers Intel and AMD alike. The NVIDIA
33 # wrappers are the ones that need --impure (they read the host kernel
34 # 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 ago35 #
36 # Built from nixGL's default.nix rather than taken from its flake
37 # outputs, for the one argument the flake hardcodes on: `enable32bits`,
38 # which on x86_64 puts a second, i686 copy of mesa, its LLVM, and
39 # intel-media-driver into the wrapper. frq is 64-bit on both halves —
40 # the Rust cdylibs and the Chez runtime — so nothing here ever opens the
41 # 32-bit driver, and carrying it is most of the dev shell's closure.
42 nixGLFor = pkgs: (import nixgl {
43 inherit pkgs;
44 enable32bits = false;
45 }).nixGLIntel;
Run this tree on a native half the builders made a32699e nandi 17d ago46
Build the APK from the flake, at the rev flake.lock pins 77d4bec nandi 7d ago47 # The Android SDK wants two things `nixpkgs.legacyPackages` cannot give:
48 # `allowUnfree`, because the SDK's own licence is not free, and
49 # `android_sdk.accept_license`, which is how you say so in a file rather
50 # than at a prompt a build has no terminal for. Neither can be set on a
51 # legacyPackages attribute after the fact, so this is a second import of
52 # the same locked nixpkgs rather than a second nixpkgs.
53 #
54 # This used to live in `just apk` as a `nix build --impure --expr` with
55 # `builtins.getFlake "github:NixOS/nixpkgs/nixos-unstable"` inside it —
56 # which fetched whatever nixos-unstable was that morning, not what
57 # flake.lock pins, so the SDK under the APK and the nixpkgs under
58 # everything else were free to drift apart. Here they are the same rev.
59 androidPkgsFor = system: import nixpkgs {
60 inherit system;
61 config = {
62 allowUnfree = true;
63 android_sdk.accept_license = true;
64 };
65 };
66
67 # Only the floor Gradle stands on. It installs build-tools and a platform
68 # into ANDROID_HOME itself as it goes — see `just apk` for why that means
69 # a writable copy — so composing more of them here buys nothing.
70 #
71 # includeNDK = false deliberately: the app is Dart and path_provider is
72 # platform channels, so there is no native code to need one, and asking
73 # for it is a few hundred megabytes and a Gradle fetch of that exact NDK.
74 androidSdkFor = system:
75 let android = androidPkgsFor system; in
76 (android.androidenv.composeAndroidPackages {
77 cmdLineToolsVersion = "13.0";
78 buildToolsVersions = [ "34.0.0" ];
79 platformVersions = [ "35" "34" ];
80 includeNDK = false;
81 }).androidsdk;
82
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago83 in
84 {
85 packages = forEachSystem (pkgs:
86 let
87 inherit (pkgs) lib;
88 in
89 {
Build the APK from the flake, at the rev flake.lock pins 77d4bec nandi 7d ago90 # The Android SDK `just apk` copies into flutter/.home. A package
91 # rather than something the recipe evaluates inline, so that
92 # `nix build .#android-sdk` is how you pre-warm it and `nix flake
93 # show` admits it exists.
94 android-sdk = androidSdkFor pkgs.stdenv.hostPlatform.system;
95
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago96 # There were `appimage` outputs here, and what they were for was a
97 # host without Nix: they squashed the whole closure into one
98 # runnable file, Mesa included, and the Mesa was not waste — off
99 # NixOS the launcher goes through nixGL, which needs a store Mesa to
100 # put the host's driver in front of. Nothing asks for that shape any
101 # more, and they were the last thing evaluating nix-appimage, which
102 # is why that input is gone too.
Four builds, four hashes, and the one file that was doing it b3b3cf6 nandi 5d ago103
Build the Flutter desktop GUI with nix, sandbox and all 6ca9b5a nandi 7d ago104 # Everything `clojure -M:cljd compile` would otherwise reach the
105 # network for, fetched once and hashed.
106 #
107 # The compile needs three caches, and the reason this is one
108 # derivation rather than three is that only one of them is obvious.
109 # Maven and gitlibs are the ordinary tools.deps pair. The third is
110 # ClojureDart's own: `ensure-cljd-analyzer!` writes a *second*, whole
111 # pub project to `.clojuredart/cache/<cljd sha>/cljd_helper`, runs
112 # `pub add analyzer` in it, and then runs `bin/analyzer.dart` out of
113 # it for the duration of the compile — so a sandbox needs that
114 # project already resolved, not just the app's dependencies.
115 #
116 # Fixed-output, so it is allowed the network the rest of the build is
117 # not. What that costs is a hash to maintain, and the thing worth
118 # being exact about is *when*: this derivation never sees frq's
119 # source. It compiles a three-line throwaway project against the same
120 # `flutter/deps.edn` and the same `flutter/pubspec.yaml`, so the hash
121 # moves when a dependency moves and not when a screen changes. A
122 # stub, rather than `-P` and a hand-built analyzer dir, because
123 # running the real compiler once is the only way to be sure the
124 # caches are the ones it actually wants.
125 #
126 # PUB_CACHE lands in $out on purpose. The package_config.json inside
127 # cljd_helper carries absolute paths to whatever resolved it, so
128 # resolving into a build directory would bake in paths that stop
129 # existing the moment this derivation finishes. Pointed at $out they
130 # are store paths, and still true.
131 cljd-deps =
132 let
133 flutterPkg = pkgs.flutter;
134 in
135 pkgs.stdenvNoCC.mkDerivation {
136 name = "frq-cljd-deps";
137 dontUnpack = true;
138
139 nativeBuildInputs = [
140 pkgs.clojure
141 pkgs.jdk17
142 flutterPkg
143 pkgs.git
144 pkgs.cacert
145 ];
146
147 buildCommand = ''
148 export HOME="$NIX_BUILD_TOP/home"
149 # Resolved in the build directory and copied to $out at the
150 # end, never written there directly. A fixed-output derivation
151 # may not reference a store path and its own output is a store
152 # path, so pub writing its cache's absolute location into its
153 # own metadata is enough to fail the check.
154 cache="$NIX_BUILD_TOP/cache"
155 export PUB_CACHE="$cache/pub-cache"
156 export GITLIBS="$cache/gitlibs"
157 export SSL_CERT_FILE="${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
158 mkdir -p "$HOME" "$PUB_CACHE" "$GITLIBS" "$cache/m2"
159
Make ../common a dependency rather than a path 95d91a2 nandi 7d ago160 # The stub: our dependency files, nothing of our source.
161 # ../common is a :local/root dependency now, so it has to exist
162 # *and* carry a deps.edn for tools.deps to resolve an empty
163 # directory with that one file in it is enough.
Build the Flutter desktop GUI with nix, sandbox and all 6ca9b5a nandi 7d ago164 proj="$NIX_BUILD_TOP/stub"
165 mkdir -p "$proj/src/stub" "$NIX_BUILD_TOP/common"
Make ../common a dependency rather than a path 95d91a2 nandi 7d ago166 cp ${./common/deps.edn} "$NIX_BUILD_TOP/common/deps.edn"
Build the Flutter desktop GUI with nix, sandbox and all 6ca9b5a nandi 7d ago167 cp ${./flutter/deps.edn} "$proj/deps.edn"
168 cp ${./flutter/pubspec.yaml} "$proj/pubspec.yaml"
Four builds, four hashes, and the one file that was doing it b3b3cf6 nandi 5d ago169 # The lock, or `pub get` resolves against pub.dev and takes
170 # whatever satisfies the ranges today. Every build then fetches
171 # a slightly different set and the fixed-output hash is a
172 # promise nothing can keep.
173 cp ${./flutter/pubspec.lock} "$proj/pubspec.lock"
174 chmod u+w "$proj/deps.edn" "$proj/pubspec.yaml" "$proj/pubspec.lock"
Build the Flutter desktop GUI with nix, sandbox and all 6ca9b5a nandi 7d ago175 cat > "$proj/src/stub/main.cljd" <<'EOF'
176 (ns stub.main)
177 (defn main [] nil)
178 EOF
179
180 cd "$proj"
181 # `:main` has to name the stub, or the compiler goes looking for
182 # frq.main in a tree that is not here.
183 sed -i 's/:main frq\.main/:main stub.main/' deps.edn
184
185 flutter config --no-analytics &>/dev/null || true
186 flutter config --enable-linux-desktop >/dev/null || true
187
188 clojure -Sdeps '{:mvn/local-repo "'"$cache"'/m2"}' -M:cljd compile
189
190 # What the compile left behind, and only that. The analyzer
191 # project is keyed by the ClojureDart sha, so the directory
192 # under cache/ is copied wholesale rather than named here.
193 mkdir -p "$out/clojuredart"
194 cp -r .clojuredart/cache "$out/clojuredart/cache"
195 cp -r "$cache/m2" "$out/m2"
196 cp -r "$cache/gitlibs" "$out/gitlibs"
197 cp -r "$PUB_CACHE" "$out/pub-cache"
198
199 # A fixed-output derivation may not reference a store path, and
200 # a resolved pub project is nothing but store paths:
201 # package_config.json names the Flutter SDK and every package
202 # in the cache by absolute path. So the analyzer project ships
203 # *unresolved* its pubspec and its analyzer.dart and nothing
204 # else and `flutter pub get --offline` re-resolves it against
205 # this cache at build time, where naming the store is allowed.
206 find "$out" \( -name '.dart_tool' -o -name '.flutter-plugins' \
207 -o -name '.flutter-plugins-dependencies' \) -prune -exec rm -rf {} +
208 find "$out" -name '.packages' -delete
209
210
211 # A fixed-output hash is a promise that two runs agree, so
212 # everything a tool writes *about* a run rather than about a
213 # dependency has to go: pub's log carries timestamps, Maven
214 # rewrites its resolution metadata on every resolve, and
215 # tools.gitlibs keeps bare clones it only needs in order to
216 # make a checkout. None of it is read offline.
Four builds, four hashes, and the one file that was doing it b3b3cf6 nandi 5d ago217 #
218 # active_roots is the one that was actually breaking this. Pub
219 # records the project directories using the cache, sharded by
220 # a hash of the path, and $NIX_BUILD_TOP is different on every
221 # run -- so two builds whose hosted/ trees were byte-identical
222 # still disagreed, purely over which directory had asked. Four
223 # builds gave four hashes until this went.
Build the Flutter desktop GUI with nix, sandbox and all 6ca9b5a nandi 7d ago224 rm -rf "$out/pub-cache/log" "$out/pub-cache/_temp" \
225 "$out/pub-cache/git" "$out/pub-cache/global_packages" \
Four builds, four hashes, and the one file that was doing it b3b3cf6 nandi 5d ago226 "$out/pub-cache/bin" "$out/pub-cache/active_roots"
Build the Flutter desktop GUI with nix, sandbox and all 6ca9b5a nandi 7d ago227
228 # tools.gitlibs keeps a bare clone per URL under _repos/, and a
229 # bare clone is packfiles which two runs of the same fetch do
230 # not have to produce byte for byte. It cannot simply be
231 # deleted, because `procure` calls `ensure-git-dir` before it
232 # looks at anything else and would clone it again, over a
233 # network this has and the build that uses it does not.
234 #
235 # It does not need the objects, though. `procure` finds the sha
236 # with `match-exact` against the checkout already in libs/, so
237 # the bare repo only has to exist. Emptied and re-initialised,
238 # it is a fixed handful of files from the pinned git and the
239 # same on every run.
240 find "$out/gitlibs/_repos" -name HEAD | while read -r head; do
241 repo="$(dirname "$head")"
242 rm -rf "$repo"
243 git init --bare -q "$repo"
244 # The sample hooks are shell scripts, so they carry a
245 # `#!/nix/store/.../bash` line which is exactly the kind of
246 # store reference a fixed-output derivation may not hold. An
247 # empty bare repo nothing ever runs has no use for them.
248 rm -rf "$repo/hooks"
249 done
250 # pub's version listings, which record when they were fetched.
251 # This is the one that actually moved between two runs of this
252 # derivation: the package sources under hosted/ were identical
253 # and the listings beside them were not. Nothing offline reads
254 # them a resolution that already has every package on disk
255 # never asks pub.dev what versions exist.
256 find "$out/pub-cache" -name '.cache' -type d -prune -exec rm -rf {} +
257 find "$out/m2" \( -name '*.lastUpdated' -o -name '_remote.repositories' \
258 -o -name 'resolver-status.properties' -o -name '*.part' \
259 -o -name 'maven-metadata-*.xml*' \) -delete
260 find "$out" \( -name '.DS_Store' -o -name '*.log' -o -name '.git' \) \
261 -prune -exec rm -rf {} +
262 find "$out" -type d -empty -delete
263 chmod -R u+w "$out"
264
265 # Last, after every cleanup above: anything still naming the
266 # store fails the fixed-output check, and the error names one
267 # path out of thousands of files. This names the files.
268 if refs="$(grep -rlI /nix/store "$out" 2>/dev/null)" && [ -n "$refs" ]; then
269 echo "cljd-deps: these still reference the store:" >&2
270 echo "$refs" | head -20 >&2
271 fi
272
273 # If two runs disagree, this says which half to look in. Cheap,
274 # and the alternative is a hash mismatch with nothing attached.
275 for d in "$out"/*; do
276 echo "cljd-deps subtree $(basename "$d") $( (cd "$d" && find . -type f \
277 -exec sha256sum {} + | sort -k2 | sha256sum) )" >&2
278 done
279 for d in "$out"/pub-cache/*/*; do
280 [ -d "$d" ] || continue
Four builds, four hashes, and the one file that was doing it b3b3cf6 nandi 5d ago281 # The path relative to $out, not the basename: two runs that
282 # disagree here disagree about *which* directory exists, and
283 # a bare `09` names nothing you can go and look at.
284 echo "cljd-deps pub ''${d#$out/} $( (cd "$d" && find . -type f \
Build the Flutter desktop GUI with nix, sandbox and all 6ca9b5a nandi 7d ago285 -exec sha256sum {} + | sort -k2 | sha256sum) )" >&2
286 done
287 '';
288
289 outputHashMode = "recursive";
290 outputHashAlgo = "sha256";
291 # Moves when flutter/deps.edn or flutter/pubspec.yaml move, and
292 # not when frq's own source does — see the stub above.
Four builds, four hashes, and the one file that was doing it b3b3cf6 nandi 5d ago293 outputHash = "sha256-qSGx7WFdVyV7yu4R+EjiQHZcLqwDjYSlohiZcXB43DY=";
Build the Flutter desktop GUI with nix, sandbox and all 6ca9b5a nandi 7d ago294 };
295
296 # The Flutter desktop GUI, built rather than run out of the tree.
297 #
298 # `just flutter-desktop` is the working-tree loop and this is its
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago299 # opposite number: the source is the flake's, the output is a store
300 # path, and the build is a sandbox with no network. It is the first thing here that builds
Build the Flutter desktop GUI with nix, sandbox and all 6ca9b5a nandi 7d ago301 # purely — the APK cannot, because Gradle fetches as it goes.
302 #
303 # Two stages, because the Dart does not exist until ClojureDart writes
304 # it. `preBuild` runs the compiler over `flutter/src` and `common/`
305 # with `--offline`, out of the caches `cljd-deps` fetched; everything
306 # after that is an ordinary Flutter application as far as nixpkgs is
307 # concerned.
308 #
309 # The caches are copied in rather than used where they lie. Maven,
310 # tools.gitlibs and pub all expect to be able to write to their own
311 # cache — a lock file, a resolved marker — and the store is read-only,
312 # so pointing them at $out of a fixed-output derivation fails in three
313 # different ways at three different depths.
314 #
315 # `src` is the whole tree and not `flutter/`: `flutter/deps.edn` puts
316 # `../common` on the classpath, which is the entire point of that
317 # directory, and a source root of `flutter/` would leave the screens
318 # outside it.
319 flutter-desktop-unwrapped = pkgs.flutter.buildFlutterApplication rec {
320 pname = "frq-flutter";
321 version = "0.1.0";
322
323 src = lib.cleanSourceWith {
324 src = ./.;
The window opens, and a devShell that remembers what it built c20643a nandi 5d ago325 # Two trees, and only two: `flutter/` is the app and `common/`
326 # is the screens its deps.edn puts on the classpath. The root is
327 # still the source root because of that `../common`, but letting
328 # the *whole* root in means every file in the repo is an input —
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago329 # so editing flake.nix or CLAUDE.md
The window opens, and a devShell that remembers what it built c20643a nandi 5d ago330 # invalidated the entire Dart compile and paid ten minutes for a
331 # change the Flutter build cannot even see.
332 #
333 # Matched on the path relative to the root rather than on
334 # basename: `src` as a basename would also exclude `flutter/src`
335 # and `common/src`, which is everything that matters.
336 filter =
337 let root = toString ./.; in
338 path: type:
339 let
340 rel = lib.removePrefix (root + "/") (toString path);
341 inTree = d: rel == d || lib.hasPrefix (d + "/") rel;
342 in
343 (inTree "flutter" || inTree "common")
344 # Build trees and caches, which are large, machine-specific
345 # and would make every one of them a new store path.
346 && !(builtins.elem (baseNameOf path) [
347 "build" ".home" ".clojuredart" ".cpcache" "cljd-out"
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago348 ".dart_tool" "result" ".git" "buck-out"
The window opens, and a devShell that remembers what it built c20643a nandi 5d ago349 ]);
Build the Flutter desktop GUI with nix, sandbox and all 6ca9b5a nandi 7d ago350 };
351 sourceRoot = "source/flutter";
352
353 # Read at eval time, so the lock in git is the lock that is built.
354 autoPubspecLock = ./flutter/pubspec.lock;
355
356 # git, because tools.deps resolves the ClojureDart dependency through
357 # tools.gitlibs even when every byte of it is already on disk — see
358 # the _repos note in cljd-deps.
359 nativeBuildInputs = [ pkgs.clojure pkgs.jdk17 pkgs.git ];
360
361 preBuild = ''
362 export PUB_CACHE="$NIX_BUILD_TOP/pub-cache"
363 export GITLIBS="$NIX_BUILD_TOP/gitlibs"
364 cp -r ${self.packages.${pkgs.stdenv.hostPlatform.system}.cljd-deps}/pub-cache "$PUB_CACHE"
365 cp -r ${self.packages.${pkgs.stdenv.hostPlatform.system}.cljd-deps}/gitlibs "$GITLIBS"
366 cp -r ${self.packages.${pkgs.stdenv.hostPlatform.system}.cljd-deps}/m2 "$NIX_BUILD_TOP/m2"
367 mkdir -p .clojuredart
368 cp -r ${self.packages.${pkgs.stdenv.hostPlatform.system}.cljd-deps}/clojuredart/cache .clojuredart/cache
369 chmod -R u+w "$PUB_CACHE" "$GITLIBS" "$NIX_BUILD_TOP/m2" .clojuredart
370
371 # Resolve the analyzer project here rather than in cljd-deps,
372 # which was not allowed to name the store. Offline, out of the
373 # cache that derivation did fetch. ClojureDart only reaches for
374 # the network when `bin/analyzer.dart` is missing, and it is not.
375 for helper in .clojuredart/cache/*/cljd_helper; do
376 ( cd "$helper" && flutter pub get --offline )
377 done
378
379 # --offline is what keeps `pub get` out of a sandbox that has no
380 # network; the analyzer project it would otherwise resolve is
381 # already in .clojuredart, put there by cljd-deps.
382 clojure -Sdeps "{:mvn/local-repo \"$NIX_BUILD_TOP/m2\"}" \
383 -M:cljd compile --offline
384 '';
385
386 meta = {
387 description = "frq's screens on Flutter's Linux target (no GL launcher)";
388 mainProgram = "frq";
389 platforms = systems;
390 };
391 };
392
393 # The same shape as `frq` above: a launcher, and a package that is a
394 # symlink to it. The reason is the same one `frqScript` gives — on
395 # NixOS the store's Mesa is the system's and the window opens, and
396 # anywhere else the real driver is the host's, so the process is
397 # handed to nixGL. Without it the store build dies on a distrobox
398 # Arch with "No provider of eglGetPlatformDisplayEXT found", which is
399 # that failure wearing an EGL hat.
400 #
401 # A wrapper *around* the built application rather than a `postFixup`
402 # inside it, because buildFlutterApplication's own dartFixupHook runs
403 # after postFixup and rewrites `bin/frq` — so anything done to that
404 # path from inside is undone on the way out.
405 flutter-desktop =
406 let
407 unwrapped =
408 self.packages.${pkgs.stdenv.hostPlatform.system}.flutter-desktop-unwrapped;
The window opens, and a devShell that remembers what it built c20643a nandi 5d ago409
410 # buildFlutterApplication's own wrapper appends a bare `/lib` to
411 # LD_LIBRARY_PATH — the host's, not the bundle's. Off NixOS that
412 # is a foreign library directory in front of nothing, and the app
413 # dies in the loader before main: first
414 #
415 # /lib/libc.so.6: undefined symbol: __pointer_chk_guard
416 #
417 # and, once the store's glibc is put ahead of it,
418 #
419 # libc.so.6: version `GLIBC_2.43' not found
420 # (required by /lib/libglib-2.0.so.0)
421 #
422 # which is the same bug wearing the other hat: the host's glib
423 # against the store's glibc. Ordering cannot fix a mixture, so
424 # the entry goes rather than moves. The wrapper is generated, so
425 # this edits a copy and asserts the edit landed — a silent miss
426 # here is a runtime failure on someone else's machine.
427 fixed = pkgs.runCommand "frq-flutter-wrapper" { } ''
428 mkdir -p "$out/bin"
429 sed "s|'/lib'||g" ${unwrapped}/bin/frq > "$out/bin/frq"
430 chmod +x "$out/bin/frq"
431 if grep -q "'/lib'" "$out/bin/frq"; then
432 echo "the /lib entry outlived the edit; look at the wrapper" >&2
433 exit 1
434 fi
435 '';
436
Build the Flutter desktop GUI with nix, sandbox and all 6ca9b5a nandi 7d ago437 script = pkgs.writeShellScript "frq" ''
438 runner=""
439 [ -e /run/current-system ] || runner="${nixGLFor pkgs}/bin/nixGLIntel"
The window opens, and a devShell that remembers what it built c20643a nandi 5d ago440 exec ''${runner} ${fixed}/bin/frq "$@"
Build the Flutter desktop GUI with nix, sandbox and all 6ca9b5a nandi 7d ago441 '';
442 in
443 pkgs.runCommand "frq-flutter-0.1.0"
444 {
445 meta = {
446 description = "frq's screens on Flutter's Linux target";
447 mainProgram = "frq";
448 platforms = systems;
449 };
450 }
451 ''
452 mkdir -p "$out/bin"
453 ln -s ${script} "$out/bin/frq"
454 '';
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago455 });
456
Run this tree on a native half the builders made a32699e nandi 17d ago457 devShells = forEachSystem (pkgs:
458 let
459 inherit (pkgs) lib;
460 in
461 {
The binding is Dart, and it works f7aea3b nandi 21h ago462 # Dart on its own, for the tests that need no Flutter: the FFI
463 # binding to the Nim core runs on the plain VM, and making it wait
464 # for a Flutter toolchain would throw away the reason it is fast.
465 #
466 # Flutter bundles a Dart, so this is a duplicate in one sense. It is
467 # also thirty times smaller, and the point of the boundary is that
468 # you can check it without the thing on the other side of it.
469 dart = pkgs.mkShellNoCC {
470 name = "frq-dart";
471 packages = [ pkgs.dart pkgs.just ];
A message in #test, from Nim 35994d4 nandi 20h ago472
473 # libfrqcore.so is linked against OpenSSL, and the process that
474 # dlopens it has to be able to find one. Named here rather than
475 # left to the host: a machine whose libssl is a different soname
476 # fails at `frq_init` with a message about the wrong library.
477 LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.openssl ];
478
The binding is Dart, and it works f7aea3b nandi 21h ago479 FRQ_DART = "1";
480 };
481
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago482 # Nim, for `nim/` — the portable core as a native library. Just the
483 # compiler: the core has no dependencies outside Nim's own standard
484 # library, deliberately, because a dependency here is one that has
485 # to cross-compile to every target the Dart side runs on.
486 #
487 # `nim c` shells out to a C compiler, so this is mkShell and not
488 # mkShellNoCC: stdenv brings the one Nim will find.
489 nim = pkgs.mkShell {
490 name = "frq-nim";
491 packages = [ pkgs.nim pkgs.just ];
492
A message in #test, from Nim 35994d4 nandi 20h ago493 # OpenSSL, because `-d:ssl` in nim/nim.cfg makes std/net link
494 # -lssl and -lcrypto: the IRC connection is TLS on :6697, which is
495 # the only port freeq actually listens on.
496 buildInputs = [ pkgs.openssl ];
497
498 # And on the loader path as well as the linker's. Nim resolves the
499 # OpenSSL entry points through dynlib at run time, so without this
500 # `newContext` finds nothing behind the symbol and dies with a
501 # SIGSEGV that says nothing about SSL at all.
502 LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.openssl ];
503
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago504 # The recipe's re-entry test, the way FRQ_FLUTTER_DESKTOP is the
505 # desktop one's.
506 FRQ_NIM = "1";
Run this tree on a native half the builders made a32699e nandi 17d ago507 };
Build the APK from the flake, at the rev flake.lock pins 77d4bec nandi 7d ago508
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago509 # The APK toolchain. It is not in a default shell any more because
510 # there is no default shell: what used to be one belonged to the
511 # libcosmic frontend, and a Flutter build asks for a toolchain by
512 # name.
513 #
514 # Flutter brings its own Dart, Gradle and a JDK's worth of
515 # closure: Flutter brings its own Dart, Gradle and a JDK's worth of
Build the APK from the flake, at the rev flake.lock pins 77d4bec nandi 7d ago516 # closure, and a desktop build has no use for any of it.
517 #
518 # `just apk` used to name these as `nix shell nixpkgs#clojure
519 # nixpkgs#jdk17 nixpkgs#flutter`, which is the flake registry's
520 # nixpkgs and not this flake's — so the Flutter under the APK
521 # floated while everything else was locked. Same three packages,
522 # from flake.lock now.
523 #
524 # JDK 17 and not newer on purpose: the Flutter template's Gradle
525 # plugin pins a Gradle that rejects a JDK it was released before,
526 # and the failure reads as an unsupported class file version rather
527 # than as a version mismatch.
528 flutter = pkgs.mkShellNoCC {
529 name = "frq-flutter";
Seed the ClojureDart caches out of the flake f2e12af nandi 7d ago530
531 # git, because tools.deps resolves the ClojureDart dependency
532 # through tools.gitlibs even when every byte of it is already in
533 # the seeded cache — the same reason flutter-desktop-unwrapped
534 # names it. The host's git has always been there to answer; naming
535 # it means the shell does not depend on that.
536 packages = [ pkgs.clojure pkgs.jdk17 pkgs.flutter pkgs.just pkgs.git ];
Build the APK from the flake, at the rev flake.lock pins 77d4bec nandi 7d ago537
538 # Where the recipe copies from. Naming it here is also what makes
539 # entering the shell build it, so the first `just apk` does not
540 # stop for a few hundred megabytes of SDK with nothing said about
541 # why.
542 FRQ_ANDROID_SDK =
543 "${androidSdkFor pkgs.stdenv.hostPlatform.system}/libexec/android-sdk";
Seed the ClojureDart caches out of the flake f2e12af nandi 7d ago544
545 # The Maven, gitlibs and pub caches the ClojureDart compile would
546 # otherwise fetch, plus the analyzer project it writes under
547 # .clojuredart. Here for both of FRQ_ANDROID_SDK's reasons: it is
548 # where the recipe copies from, and naming it is what makes
549 # entering the shell build it.
550 #
551 # The compile still runs online. These are a warm start and not a
552 # pin — `--offline` would be, and would turn adding a line to
553 # flutter/deps.edn into a re-hash of cljd-deps before anything
554 # compiled again. The sandbox build takes that trade because it
555 # has no network; the loop someone edits in should not.
556 FRQ_CLJD_DEPS = "${self.packages.${pkgs.stdenv.hostPlatform.system}.cljd-deps}";
Build the APK from the flake, at the rev flake.lock pins 77d4bec nandi 7d ago557 };
Paint the desktop with Flutter too, beside libcosmic fc5f63c nandi 7d ago558
559 # The other desktop GUI. Same ClojureDart half as the APK — one
560 # `clojure -M:cljd compile`, one flutter/src — over Flutter's Linux
561 # target instead of its Android one, so `flutter/linux/` is the
562 # runner and CMake and Ninja are the build rather than Gradle.
563 #
564 # A separate shell from `flutter` rather than one that carries both,
565 # because the halves are disjoint: this wants GTK and a C++ toolchain
566 # and no JDK, and the APK wants a JDK and an SDK and no GTK. Sharing
567 # them would mean every desktop build paying for a few hundred
568 # megabytes of Android SDK it never opens, which is the same argument
569 # that keeps Flutter out of the default shell.
570 #
571 # mkShell and not mkShellNoCC, unlike every other shell here: this is
572 # the one that actually compiles C++. stdenv brings the compiler, and
573 # gtk3 in buildInputs is what puts its .pc file where the runner's
574 # `pkg_check_modules(GTK gtk+-3.0)` can find it.
575 flutter-desktop = pkgs.mkShell {
576 name = "frq-flutter-desktop";
577
578 # clojure and flutter are the APK shell's, and deliberately the
579 # same two: the Dart that runs here is generated by the same
580 # compiler from the same source, and a second Flutter version
581 # under it would be a second set of engine artifacts and a second
582 # answer to "does the phone build match the desktop one".
583 nativeBuildInputs = [
584 pkgs.clojure
585 pkgs.flutter
586 pkgs.just
587 pkgs.cmake
588 pkgs.ninja
589 pkgs.pkg-config
590 ];
591
592 # gtk3 is the runner's own dependency; the rest are url_launcher's
593 # Linux implementation, which is a GTK plugin compiled into the
594 # bundle. path_provider needs nothing here — its Linux half is
595 # pure Dart over the XDG directories.
596 buildInputs = [ pkgs.gtk3 pkgs.glib ];
597
The window connects 1d62d1a nandi 20h ago598 # Where libfrqcore.so's OpenSSL lives. A named variable and NOT
599 # LD_LIBRARY_PATH, deliberately: this shell also runs Flutter
600 # through nixGL, which does its own careful things to the loader
601 # path, and a blanket LD_LIBRARY_PATH here is the sort of thing
602 # that breaks GL on one machine and not another. The `nim-spike`
603 # recipe prepends this for the app it launches and nothing else.
604 FRQ_OPENSSL_LIB = lib.makeLibraryPath [ pkgs.openssl ];
605
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago606 # Flutter paints through GL, and off NixOS the driver that can do
607 # that is the host's, not the store's.
Paint the desktop with Flutter too, beside libcosmic fc5f63c nandi 7d ago608 NIXGL = "${nixGLFor pkgs}/bin/nixGLIntel";
609
Seed the ClojureDart caches out of the flake f2e12af nandi 7d ago610 # The `flutter` shell's, deliberately the same one and for the
611 # same reason clojure and flutter are: the ClojureDart half of
612 # both builds is one compile over one deps.edn, so a second set of
613 # caches would be a second answer to what it resolved against.
614 # This recipe reads the variable directly — it is inside this
615 # shell before it does any of the work — where `just apk` reaches
616 # for the flake output itself.
617 FRQ_CLJD_DEPS = "${self.packages.${pkgs.stdenv.hostPlatform.system}.cljd-deps}";
618
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago619 # The recipe's re-entry test. Nothing else sets it, so `just flutter-desktop`
Paint the desktop with Flutter too, beside libcosmic fc5f63c nandi 7d ago620 # outside the shell re-enters and lands back on the same recipe —
621 # no flag to forget, and no second code path for someone who runs
622 # `nix develop .#flutter-desktop --command just flutter-desktop`
623 # by hand.
624 FRQ_FLUTTER_DESKTOP = "1";
625 };
A third target, and the seam that was already waiting for it f54ca45 nandi 2d ago626
Three tarballs where a devShell was 5ce66d5 nandi yesterday627 # No `flutter-web` shell here any more. The web target was the one
628 # that needed nothing of the host -- no JDK and no Android SDK as
629 # the APK wants, no GTK and no C++ and no nixGL as the desktop one
630 # does -- and a devShell whose only job is to hand over a Dart and
631 # a JVM is a devShell that a pinned tarball can replace. It did:
632 # `tools/toolchain.sh` fetches Flutter, a JDK and the Clojure CLI by
633 # sha256, `tools/build-web.sh` builds out of them, and
634 # `.modal/flutter-web/` runs that same script on a plain Debian
635 # image with no store to populate.
A third target, and the seam that was already waiting for it f54ca45 nandi 2d ago636 #
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago637 # The two shells above stay. What they supply is a host toolchain,
Three tarballs where a devShell was 5ce66d5 nandi yesterday638 # which is exactly what nix is better at than a tarball.
Run this tree on a native half the builders made a32699e nandi 17d ago639 });
640
Build the client with Nix, not just a shell for it ca3afbe nandi 19d ago641 };
642}