nandi/frqpublic Fork 0
0ee578676a434d68b1e7a80380ba0a717d0272f7
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.

justfile · 325 lines · 14.2 KBMakefile Blame HistoryRaw
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago1# The work is here now, in recipe bodies, where it used to be in scripts/ —
2# babashka scripts run through a scripts/bb that went looking for a babashka.
3# That indirection bought one thing worth having, a shared way to reach nix on
4# a host that keeps it in a container, and `nix` below is the whole of it.
5#
6# Every recipe that runs frq has the same shape: outside the dev shell, re-enter
7# it and come back to this same recipe; inside, hand jolt the deps overrides and
8# the library path the shell exported. The re-entry test is JOLT_NATIVE_LIB,
9# which only the shell sets — no flag to forget, and no second code path for
10# someone who runs `nix develop --command just run` by hand.
Take glimmer-vidya from gitlab, so a stranger can build this 5c40e75 nandi 19d ago11
Write the build in babashka, and pin the babashka 34832a8 nandi 18d ago12set shell := ["bash", "-euo", "pipefail", "-c"]
A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago13
Give the terminal's classpath an nREPL, and hand recipes their arguments 664397d nandi 10d ago14# Every recipe below is a `#!` script and passes its arguments on with "$@".
15# Without this that is empty in one — just interpolates into a shebang recipe
16# rather than handing it argv — and `just tui --headless` silently ran the
17# terminal instead.
18set positional-arguments
19
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago20# nix is not on every host this runs on: on the machine these recipes were
21# written for it lives in an Arch distrobox, at the same path — which is why
22# the container is entered rather than the tree copied into it. See CLAUDE.md.
23nix := `command -v nix >/dev/null 2>&1 && echo nix || echo "distrobox enter arch -- nix"`
24
25# --max-jobs 0 is what sends the work to the `builders` entry rather than
26# compiling it here. Left to the default, nix prefers the local machine, and a
27# cold jolt-native is egui, openh264 and quinn on a laptop — for a derivation a
28# remote builder has likely built already. FRQ_MAX_JOBS=auto is the way out on
29# a machine with no builder configured.
30jobs := env("FRQ_MAX_JOBS", "0")
31
A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago32default:
33 @just --list
34
Paint the phone in COSMIC's own theme, read from cosmic-config ba4e71b nandi 7d ago35# Re-read the COSMIC theme into the APK.
36#
37# libcosmic asks cosmic-config for the accent and the surfaces at run time, so
38# `just run` already follows COSMIC Settings as it changes. A phone has no
39# cosmic-config, so the APK carries them instead — read here, on the machine
40# that has them, and compiled in. That is the one real difference between the
41# two, and it is why the generated file is in git rather than gitignored: a
42# checkout on a machine with no COSMIC still builds.
43#
44# Run it after changing the theme in COSMIC Settings, then `just apk`.
45theme:
46 #!/usr/bin/env bash
47 set -euo pipefail
48 cd "{{justfile_directory()}}"
49 python3 tools/cosmic2cljd.py flutter/src/frq/theme/cosmic.cljd
50
Build the APK, out of ClojureDart and Flutter e2b0e6b nandi 7d ago51# The APK: ClojureDart compiled to Dart, then Flutter's Gradle build.
52#
53# Impure on purpose, and worth saying why rather than leaving it to be
54# discovered. Gradle resolves its own dependencies over the network and
55# installs build-tools and a platform into ANDROID_HOME as it goes, so it
56# cannot run in a sandbox and cannot write to the store. What nix gives here
57# is the toolchain — clojure, a JDK, Flutter, and an SDK composed by
58# androidenv — and the recipe copies that SDK somewhere writable
59# (flutter/.home) for Gradle to finish off. That copy and everything Gradle
60# leaves behind are gitignored.
61#
62# No ndkVersion in android/app/build.gradle.kts, for the same reason: the
63# Flutter template sets it, setting it makes Gradle fetch that exact NDK, and
64# there is no native code here to need one.
65#
66# just apk build the debug APK
67# just apk install build it and put it on a connected device
68# just apk run install and launch
69# just apk log logcat, filtered to this app
70apk action="build":
71 #!/usr/bin/env bash
72 set -euo pipefail
73 cd "{{justfile_directory()}}/flutter"
74
Build the APK from the flake, at the rev flake.lock pins 77d4bec nandi 7d ago75 # The flake's, not an --impure --expr against whatever nixos-unstable is
76 # today: the licence config the SDK needs lives in `androidPkgsFor` now,
77 # so this is an ordinary output at the rev flake.lock pins.
78 sdk="$(nix build --no-link --print-out-paths \
79 "{{justfile_directory()}}#android-sdk")/libexec/android-sdk"
Build the APK, out of ClojureDart and Flutter e2b0e6b nandi 7d ago80
81 export HOME="$PWD/.home"
82 export ANDROID_HOME="$HOME/android-sdk"
83 export ANDROID_SDK_ROOT="$ANDROID_HOME"
84 mkdir -p "$HOME"
85
86 # Gradle writes into ANDROID_HOME, so it is a copy rather than the store
87 # path. Made once and kept: re-copying would throw away the build-tools
88 # and platform Gradle installed into it on the last run.
89 if [ ! -d "$ANDROID_HOME" ]; then
90 cp -r "$sdk" "$ANDROID_HOME"
91 chmod -R u+w "$ANDROID_HOME"
92 fi
93
Build the APK from the flake, at the rev flake.lock pins 77d4bec nandi 7d ago94 # Also the flake's. `nix shell nixpkgs#...` read the registry, which is a
95 # different and unlocked nixpkgs — the Flutter that built the APK could
96 # move under it without flake.lock changing a line.
97 flutter="nix develop {{justfile_directory()}}#flutter --command"
Build the APK, out of ClojureDart and Flutter e2b0e6b nandi 7d ago98
99 $flutter clojure -M:cljd compile
100
101 # Rewritten every run: it carries absolute store paths, and the flutter
102 # one moves whenever nixpkgs does.
103 $flutter flutter config --android-sdk "$ANDROID_HOME" >/dev/null
104
105 apk=build/app/outputs/flutter-apk/app-debug.apk
106 adb="${ADB:-$ANDROID_HOME/platform-tools/adb}"
107
108 case "{{action}}" in
109 build) $flutter flutter build apk --debug ;;
110 install) $flutter flutter build apk --debug && "$adb" install -r "$apk" ;;
111 run) $flutter flutter build apk --debug && "$adb" install -r "$apk" \
112 && "$adb" shell monkey -p uk.nandi.frq -c android.intent.category.LAUNCHER 1 ;;
113 log) "$adb" logcat -s flutter ;;
114 *) echo "usage: just apk [build|install|run|log]" >&2; exit 1 ;;
115 esac
116
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago117# Two halves, and the split is the point. The frq source is the files on disk,
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago118# uncommitted edits and all. Everything under it — jolt, glimmer, glimmer-cosmic
119# and the native objects — is the flake's, built rather than fetched.
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago120#
121# Deliberately not `nix run .#frq`. That builds the flake's own copy of the
122# source, which is the tree as git has it — so an edit that has not been
123# committed, or committed on a branch the command was not pointed at, runs as
124# whatever was there before, silently. A run meant to answer "does my change
125# work" has to be the files on disk.
126#
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago127# libcosmic paints through wgpu, so this needs nixGL off NixOS for the same
128# reason the window always did: the real driver is the host's.
129#
130# There is no jvui and no vidya here any more — both were experiments. The
131# window is libcosmic and the terminal is libjolttui, and those are the two.
132#
Run this tree on a native half the builders made a32699e nandi 17d ago133# The app: this tree's source on the flake's everything-else, in the dev shell.
A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago134run *args:
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago135 #!/usr/bin/env bash
136 set -euo pipefail
137 cd "{{justfile_directory()}}"
138 if [ -z "${JOLT_NATIVE_LIB:-}" ]; then
139 exec {{nix}} develop . --max-jobs {{jobs}} --command just run "$@"
140 fi
141
142 deps="{:deps {jolt-lang/glimmer {:local/root \"$GLIMMER_SRC\"}"
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago143 deps="$deps nandi/glimmer-cosmic {:local/root \"$GLIMMER_COSMIC_SRC\"}}}"
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago144
145 export LD_LIBRARY_PATH="$JOLT_NATIVE_LIB:$FRQ_LIB_PATH${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
Paint the same screens into a terminal ab83b42 nandi 17d ago146
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago147 runner=()
148 [ -e /run/current-system ] || runner=("$NIXGL")
149
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago150 exec "${runner[@]}" jolt -Sdeps "$deps" -m frq.cosmic "$@"
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago151
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago152# `run` with the other backend under it. Only libjolttui: `frq.app` names no
153# backend at all any more, and `frq.tui` requires glimmer-tui so the one
154# installed is the terminal.
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago155#
156# No nixGL here, unlike `run`: a terminal wants nothing from the host's GL
157# driver, which is the reason this output exists on machines that have none.
158#
Check what common/ may contain, on every push 86c2e1b nandi 7d ago159# What may appear in common/, checked — the half of the tree both backends
160# compile. Needs nothing built: it reads the source, so it is the one check
161# that runs anywhere, and CI runs exactly this.
162check-common:
163 #!/usr/bin/env bash
164 python3 tools/check-common.py common
165
Paint the same screens into a terminal ab83b42 nandi 17d ago166# The same screens in a terminal. `just tui --headless` prints one screenshot.
167tui *args:
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago168 #!/usr/bin/env bash
169 set -euo pipefail
170 cd "{{justfile_directory()}}"
171 if [ -z "${JOLT_NATIVE_LIB:-}" ]; then
172 exec {{nix}} develop . --max-jobs {{jobs}} --command just tui "$@"
173 fi
174
175 deps="{:deps {jolt-lang/glimmer {:local/root \"$GLIMMER_SRC\"}"
176 deps="$deps nandi/glimmer-tui {:local/root \"$GLIMMER_TUI_SRC\"}}}"
177
178 export LD_LIBRARY_PATH="$JOLT_NATIVE_LIB${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
Build the desktop libraries rather than fetching a release of them 0b81161 nandi 15d ago179
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago180 exec jolt -Sdeps "$deps" -m frq.tui "$@"
181
Give the terminal's classpath an nREPL, and hand recipes their arguments 664397d nandi 10d ago182# The same classpath as `tui`, with an nREPL on it instead of a `-main`: a
183# session that can require `frq.tui` and then redefine a component while it is
184# on screen, which is a second and not the minute a rebuild costs.
185#
186# just nrepl then, from an editor or a client on 7888:
187# (require (quote frq.tui)) both backends, terminal installed last
188# (frq.tui/-main "--headless" "--demo")
189# (glimmer.core/reload!) re-mount after redefining a component
190#
191# `just repl nrepl-server` is the window's half of this — the same thing minus
192# glimmer-tui. Port is nrepl-server's own positional: `just nrepl 7889`.
193nrepl *args:
194 #!/usr/bin/env bash
195 set -euo pipefail
196 cd "{{justfile_directory()}}"
197 if [ -z "${JOLT_NATIVE_LIB:-}" ]; then
198 exec {{nix}} develop . --max-jobs {{jobs}} --command just nrepl "$@"
199 fi
200
201 deps="{:deps {jolt-lang/glimmer {:local/root \"$GLIMMER_SRC\"}"
202 deps="$deps nandi/glimmer-tui {:local/root \"$GLIMMER_TUI_SRC\"}}}"
203
204 export LD_LIBRARY_PATH="$JOLT_NATIVE_LIB:$FRQ_LIB_PATH${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
205
206 exec jolt -Sdeps "$deps" nrepl-server "$@"
207
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago208# `jolt` in the repo root does not work on its own: deps.edn carries
209# :jolt/native, so every invocation here loads libvidya and libjoltmoq before it
210# reads a line, and dies naming the library if the loader cannot find them. So
211# this is `run` without the app — and `run` is this with a window's worth of
212# extra care about the GL driver.
213#
Build the desktop libraries rather than fetching a release of them 0b81161 nandi 15d ago214# A jolt with the native libraries under it: a REPL, or `just repl nrepl-server`.
215repl *args:
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago216 #!/usr/bin/env bash
217 set -euo pipefail
218 cd "{{justfile_directory()}}"
219 if [ -z "${JOLT_NATIVE_LIB:-}" ]; then
220 exec {{nix}} develop . --max-jobs {{jobs}} --command just repl "$@"
221 fi
222
223 deps="{:deps {jolt-lang/glimmer {:local/root \"$GLIMMER_SRC\"}"
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago224 deps="$deps nandi/glimmer-cosmic {:local/root \"$GLIMMER_COSMIC_SRC\"}}}"
Inline the babashka scripts into the justfile 76dcc6c nandi 11d ago225
226 export LD_LIBRARY_PATH="$JOLT_NATIVE_LIB:$FRQ_LIB_PATH${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
227
228 exec jolt -Sdeps "$deps" "$@"
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago229
230# Regenerate src/frq/moq/raw.clj from the libmoq_ffi we actually load.
231#
232# UniFFI embeds its interface metadata in the object, so `uniffi-bindgen
233# --library` reads the truth out of the .so rather than a header shipped
Fetch libmoq_ffi again, and keep the codecs out of Rust f518938 nandi 9d ago234# beside it. Those differ at one version number: the published Linux and
235# Android objects are built without moq-ffi's `audio` and `video` features
236# (206 functions, no codecs), while the shipped C header describes the Apple
237# build (230). The object is the only source this recipe will accept.
Bind libmoq_ffi from the object's own metadata, not from its header 9f081a1 nandi 9d ago238#
239# The bindgen must match the uniffi that built the object — 0.32 for moq-ffi
240# 0.3.17 — and it is built once into the scratch dir rather than pinned into
241# the flake: nothing in a normal build needs it, and a regeneration is a thing
242# done when the moq-ffi pin moves, by hand, on purpose.
243#
244# just gen-moq # the loaded library
245# just gen-moq path/to/libmoq_ffi.so
246#
247# Regenerate the libmoq_ffi bindings from the object's own embedded metadata.
248gen-moq lib="":
249 #!/usr/bin/env bash
250 set -euo pipefail
251 lib="${1:-${JOLT_NATIVE_LIB:-}/libmoq_ffi.so}"
252 [ -f "$lib" ] || { echo "no libmoq_ffi.so at $lib — pass one: just gen-moq <path>" >&2; exit 1; }
253 work="${TMPDIR:-/tmp}/frq-gen-moq"
254 mkdir -p "$work/ubg/src"
255 cat > "$work/ubg/Cargo.toml" <<'TOML'
256 [package]
257 name = "ubg"
258 version = "0.1.0"
259 edition = "2021"
260 [[bin]]
261 name = "uniffi-bindgen"
262 path = "src/main.rs"
263 [dependencies]
264 uniffi = { version = "0.32", features = ["cli"] }
265 TOML
266 echo 'fn main() { uniffi::uniffi_bindgen_main() }' > "$work/ubg/src/main.rs"
267 ( cd "$work/ubg" && cargo build --release -q )
268 ( cd "$work/ubg" && ./target/release/uniffi-bindgen generate \
269 --library "$lib" --language python --out-dir "$work/py" --no-format )
270 python3 tools/py2jolt.py "$work"/py/*.py > "$work/body.clj"
271 { sed -n '1,/^ (:require \[jolt.ffi :as ffi\]))$/p' src/frq/moq/raw.clj; echo; cat "$work/body.clj"; } > "$work/raw.clj"
272 mv "$work/raw.clj" src/frq/moq/raw.clj
273 echo "wrote src/frq/moq/raw.clj ($(grep -c '^(ffi/defcfn' src/frq/moq/raw.clj) entry points)"
Paint the desktop with Flutter too, beside libcosmic fc5f63c nandi 7d ago274
275# The other desktop GUI: the same screens, painted by Flutter instead of
276# libcosmic.
277#
278# `just run` and this one are two frontends over one tree, and the split is
279# the same one the APK already draws. Everything under `common/` — the
280# screens, the cells, `frq.io` — is shared; what differs is who paints it and
281# who answers the host. So this recipe is `just apk` with the Android half
282# taken out: the same `clojure -M:cljd compile` over the same flutter/src,
283# then Flutter's Linux target rather than its Android one. CMake and Ninja
284# instead of Gradle, `flutter/linux/` as the runner, no SDK and no JDK.
285#
286# Still impure, for one of the two reasons `apk` is: pub.dev resolution and
287# Flutter's own engine artifacts are network. What it does NOT need is the
288# writable-ANDROID_HOME dance — nothing here writes into the store — so there
289# is no `flutter/.home` on this path.
290#
291# nixGL for the reason `run` needs it and `tui` does not: Flutter paints
292# through GL, and off NixOS the driver is the host's.
293#
294# just flutter-desktop build the debug bundle
295# just flutter-desktop run build it and open the window
296flutter-desktop action="build":
297 #!/usr/bin/env bash
298 set -euo pipefail
299 cd "{{justfile_directory()}}"
300 if [ -z "${FRQ_FLUTTER_DESKTOP:-}" ]; then
301 exec {{nix}} develop .#flutter-desktop --max-jobs {{jobs}} \
302 --command just flutter-desktop "$@"
303 fi
304 cd flutter
305
306 clojure -M:cljd compile
307 flutter build linux --debug
308
309 # x64/arm64 is Flutter's own name for the host arch, not uname's.
310 case "$(uname -m)" in
311 x86_64) arch=x64 ;;
312 aarch64) arch=arm64 ;;
313 *) echo "unknown arch $(uname -m)" >&2; exit 1 ;;
314 esac
315 bundle="build/linux/$arch/debug/bundle"
316
317 case "{{action}}" in
318 build) echo "built $PWD/$bundle/frq" ;;
319 run)
320 runner=()
321 [ -e /run/current-system ] || runner=("$NIXGL")
322 exec "${runner[@]}" "$bundle/frq"
323 ;;
324 *) echo "usage: just flutter-desktop [build|run]" >&2; exit 1 ;;
325 esac