From ad76cc9101bd950db4bb8c5e12ac4e77706e2dfb Mon Sep 17 00:00:00 2001 From: nandi Date: Mon, 27 Jul 2026 23:49:33 -0700 Subject: [PATCH] Prefer Nix for gleam+erl: flake apps and local builders. Add flake apps (global, gleam-run, waydroid-gleam), harden the devShell with android-tools, and document --builders '' --max-jobs 1 so Gleam resolves deps on the host instead of remote builders. --- README.md | 36 ++++------ docs/waydroid-gleam.md | 40 +++++++++++ flake.nix | 142 ++++++++++++++++++++++++++++++++++++-- justfile | 47 +++++++------ scripts/run | 42 +++++++---- scripts/waydroid-gleam.sh | 42 +++++++++-- 6 files changed, 283 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 9feccf7..92959c4 100644 --- a/README.md +++ b/README.md @@ -37,43 +37,37 @@ Stock KDE Connect on Android works, but the UX is dated and the desktop story on - **Commands** — remote `runcommand` actions - **Settings** — mirror notifications, clipboard sync, prefer Bluetooth -## Quick start (desktop GUI) +## Quick start (Nix) -**Easiest — packaged binary (recommended on NixOS):** +Use **local Nix builds** (remote builders often cannot reach Hex): ```bash -cd ~/code/global -nix build .#global --builders '' # local builder; Gleam hits Hex -./result/bin/global # opens phone-sized window +--builders '' --max-jobs 1 ``` -**Dev loop:** - -```bash -just run # fetch renderer + gleam run (env scrub + gfx libs) -# or: -./scripts/fetch-renderer -./scripts/run -gleam test -``` +| Command | What | +|---------|------| +| `nix develop --builders '' --max-jobs 1` | Shell with `gleam` **and** `erl` + adb | +| `nix run .#global --builders '' --max-jobs 1` | Packaged desktop GUI | +| `nix run .#gleam-run --builders '' --max-jobs 1` | Dev `gleam run` (erl + Plushie env) | +| `nix run .#waydroid-gleam --builders '' --max-jobs 1 -- smoke` | Gleam CLI smoke **inside Waydroid** | +| `just run` / `just test` / `just waydroid` | Same flags via justfile | ```bash -just package # → ./result/bin/global -just run-package +cd ~/code/global +nix run .#global --builders '' --max-jobs 1 +nix develop --builders '' --max-jobs 1 -c gleam test ``` -> `gleam run -m plushie/download` may 404 on the plushie *tool* archive. -> Use `./scripts/fetch-renderer` (downloads `plushie-renderer-*` only) instead. +Bare `gleam run` on the host **dies** if `erl` is not on `PATH` — always use the flake shell/apps above. -Override the Plushie SDK with a local checkout: +Override Plushie SDK: ```bash nix build .#global --builders '' \ --override-input plushie-gleam path:$HOME/code/plushie-gleam ``` -A **390×780** window opens (phone-shaped). Use **Nearby → Pair**, open a device, try Files / Media / Notifications. - Packaged / renderer-first entry: ```bash diff --git a/docs/waydroid-gleam.md b/docs/waydroid-gleam.md index 286443b..d23c2e6 100644 --- a/docs/waydroid-gleam.md +++ b/docs/waydroid-gleam.md @@ -49,3 +49,43 @@ WAYDROID_GLEAM_EVIDENCE_DIR=/path/to/dir ./scripts/waydroid-gleam.sh all - Waydroid image here is **x86_64** — use `*_x86_64.deb` / Termux x86_64 APK, not arm64. - Plushie / Global desktop GUI is **not** run in Waydroid; this path is CLI Gleam + BEAM only. + +## If `gleam run` dies + +### On the **host** (Linux) + +``` +error: Program not found +The program `erl` was not found. +``` + +Gleam’s Erlang target needs `erl` on `PATH`. Use: + +```bash +./scripts/run # resolves Erlang + Plushie +# or +nix develop -c gleam run +nix shell nixpkgs#gleam nixpkgs#beamPackages.erlang -c gleam run +``` + +### Inside **Waydroid** via adb + +Bare `gleam run` without env fails because Termux Erlang hardcodes +`/data/data/com.termux/files/usr/...`. Always use the script smoke, or: + +```bash +./scripts/waydroid-gleam.sh smoke +``` + +Manual adb: + +```bash +adb shell ' + export PATH=/data/local/tmp/termux-usr/bin:/system/bin + export LD_LIBRARY_PATH=/data/local/tmp/termux-usr/lib + export ERL_ROOTDIR=/data/local/tmp/termux-usr/lib/erlang + export HOME=/data/local/tmp + cd /data/local/tmp/hello_gleam + gleam run +' +``` diff --git a/flake.nix b/flake.nix index 9a081aa..5ca9506 100644 --- a/flake.nix +++ b/flake.nix @@ -301,6 +301,126 @@ } ); + apps = forAllSystems ( + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + inherit (pkgs) lib; + gfxLibs = lib.makeLibraryPath ( + lib.optionals pkgs.stdenv.isLinux ( + with pkgs; + [ + wayland + libxkbcommon + libGL + vulkan-loader + libx11 + libxcursor + libxi + libxrandr + libffi + ] + ) + ); + # Dev runner: gleam + erl from the flake, optional Plushie renderer. + scrub-env = pkgs.writeText "scrub-env.py" '' + import os + for k, v in list(os.environ.items()): + try: + k.encode("latin-1") + v.encode("latin-1") + except UnicodeEncodeError: + if k.replace("_", "").isalnum(): + print(k) + ''; + gleam-run = pkgs.writeShellApplication { + name = "global-gleam-run"; + runtimeInputs = [ + pkgs.gleam + pkgs.beamPackages.erlang + pkgs.rebar3 + pkgs.beamPackages.hex + pkgs.cacert + pkgs.python3 + pkgs.curl + ]; + text = '' + set -euo pipefail + export SSL_CERT_FILE="${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" + export NIX_SSL_CERT_FILE="$SSL_CERT_FILE" + ${lib.optionalString (gfxLibs != "") '' + export LD_LIBRARY_PATH="${gfxLibs}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" + ''} + while IFS= read -r k; do + unset -v "$k" 2>/dev/null || true + done < <(python3 ${scrub-env}) + ROOT="$(pwd)" + if [ -z "''${PLUSHIE_BINARY_PATH:-}" ]; then + if [ -x "$ROOT/bin/plushie-renderer" ]; then + export PLUSHIE_BINARY_PATH="$ROOT/bin/plushie-renderer" + elif [ -x "$ROOT/result/lib/global/bin/plushie-renderer" ]; then + export PLUSHIE_BINARY_PATH="$ROOT/result/lib/global/bin/plushie-renderer" + elif [ -x "${self.packages.${system}.plushie-renderer}/bin/plushie-renderer" ]; then + export PLUSHIE_BINARY_PATH="${self.packages.${system}.plushie-renderer}/bin/plushie-renderer" + fi + fi + if [ -z "''${PLUSHIE_BINARY_PATH:-}" ] || [ ! -x "''${PLUSHIE_BINARY_PATH:-}" ]; then + if [ -x "$ROOT/scripts/fetch-renderer" ]; then + "$ROOT/scripts/fetch-renderer" + export PLUSHIE_BINARY_PATH="$ROOT/bin/plushie-renderer" + fi + fi + exec gleam run "$@" + ''; + }; + waydroid-gleam = pkgs.writeShellApplication { + name = "global-waydroid-gleam"; + runtimeInputs = [ + pkgs.curl + pkgs.gnutar + pkgs.binutils + pkgs.python3 + pkgs.android-tools + pkgs.gleam + pkgs.beamPackages.erlang + pkgs.rebar3 + pkgs.git + ]; + text = '' + set -euo pipefail + ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)" + if [ ! -x "$ROOT/scripts/waydroid-gleam.sh" ]; then + if [ -x ./scripts/waydroid-gleam.sh ]; then + ROOT="$(pwd)" + else + echo "Run from the global git checkout (needs scripts/waydroid-gleam.sh)." >&2 + exit 1 + fi + fi + exec "$ROOT/scripts/waydroid-gleam.sh" "''${@:-all}" + ''; + }; + in + { + default = { + type = "app"; + program = "${self.packages.${system}.global}/bin/global"; + }; + global = { + type = "app"; + program = "${self.packages.${system}.global}/bin/global"; + }; + gleam-run = { + type = "app"; + program = "${gleam-run}/bin/global-gleam-run"; + }; + waydroid-gleam = { + type = "app"; + program = "${waydroid-gleam}/bin/global-waydroid-gleam"; + }; + } + ); + devShells = forAllSystems ( system: let @@ -333,6 +453,10 @@ pkgs.beamPackages.hex pkgs.git pkgs.cacert + pkgs.android-tools + pkgs.curl + pkgs.python3 + pkgs.just ]; shellHook = '' export SSL_CERT_FILE="${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" @@ -340,15 +464,19 @@ ${lib.optionalString (gfxLibs != "") '' export LD_LIBRARY_PATH="${gfxLibs}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" ''} - # Prefer a sibling plushie-gleam checkout when developing next to it. - if [ -d ../plushie-gleam ] && [ -f gleam.toml ]; then - if ! grep -q 'path = "../plushie-gleam"' gleam.toml 2>/dev/null; then - echo "note: set plushie_gleam path to ../plushie-gleam for local SDK work" + # Prefer flake-built or local renderer + if [ -z "''${PLUSHIE_BINARY_PATH:-}" ]; then + if [ -x ./bin/plushie-renderer ]; then + export PLUSHIE_BINARY_PATH="$PWD/bin/plushie-renderer" + elif [ -x ./result/lib/global/bin/plushie-renderer ]; then + export PLUSHIE_BINARY_PATH="$PWD/result/lib/global/bin/plushie-renderer" fi fi - echo "global dev shell" - echo " gleam deps download && gleam test && gleam run" - echo " nix build .#global" + echo "global nix shell — gleam=$(command -v gleam) erl=$(command -v erl)" + echo " gleam test / gleam run # needs PLUSHIE_BINARY_PATH for GUI" + echo " nix run .#global --builders \"\" # packaged desktop app" + echo " nix run .#gleam-run --builders \"\"" + echo " nix run .#waydroid-gleam -- smoke" ''; }; } diff --git a/justfile b/justfile index 97a4cc9..f4d30e3 100644 --- a/justfile +++ b/justfile @@ -1,33 +1,40 @@ -# Global — Gleam + Plushie desktop companion +# Global — prefer Nix for tools (gleam + erl + adb) +# Local builds: builders off + max-jobs (remote builders often can't reach Hex). + +nix_flags := "--builders '' --max-jobs 1" default: @just --list -# Install stock plushie-renderer into bin/ -fetch-renderer: - ./scripts/fetch-renderer +# Enter flake dev shell +shell: + nix develop {{nix_flags}} -# Unit tests +# Tests (via nix so erl is on PATH) test: - gleam test + nix develop {{nix_flags}} -c gleam test + +# Desktop GUI via packaged binary (most reliable on NixOS) +run: + nix build .#global {{nix_flags}} + ./result/bin/global -# Run GUI (scrubs bad env; needs bin/plushie-renderer) -run: fetch-renderer - ./scripts/run +# Dev gleam run (Plushie + erl from flake) +run-dev: + nix run .#gleam-run {{nix_flags}} -# Packaged binary via Nix (local builder — Gleam hits Hex) +# Packaged binary only package: - nix build .#global --builders '' + nix build .#global {{nix_flags}} @echo "→ ./result/bin/global" -# Run packaged binary -run-package: package - ./result/bin/global +# Waydroid Gleam CLI smoke +waydroid: + nix run .#waydroid-gleam {{nix_flags}} -- smoke -# Dev shell -shell: - nix develop +waydroid-all: + nix run .#waydroid-gleam {{nix_flags}} -- all -# Gleam CLI smoke inside Waydroid (adb + Termux runtime tree) -waydroid-gleam: - ./scripts/waydroid-gleam.sh all +# Fetch stock plushie-renderer into ./bin (optional; package already embeds one) +fetch-renderer: + nix develop {{nix_flags}} -c ./scripts/fetch-renderer diff --git a/scripts/run b/scripts/run index a5fab62..182a6b1 100755 --- a/scripts/run +++ b/scripts/run @@ -5,6 +5,35 @@ set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$ROOT" +# --- Erlang on PATH (Gleam dies with "Program not found: erl" without it) --- +if ! command -v erl >/dev/null 2>&1; then + if command -v nix >/dev/null 2>&1; then + ERL_OUT="$( + nix --extra-experimental-features 'nix-command flakes' build --no-link \ + --print-out-paths --builders '' \ + nixpkgs#beamPackages.erlang 2>/dev/null | head -1 + )" || true + if [[ -n "${ERL_OUT:-}" && -x "$ERL_OUT/bin/erl" ]]; then + export PATH="$ERL_OUT/bin:$PATH" + fi + fi + # Common Nix store fallbacks already present on this machine + if ! command -v erl >/dev/null 2>&1; then + for d in /nix/store/*-erlang-*/bin; do + if [[ -x "$d/erl" ]]; then + export PATH="$d:$PATH" + break + fi + done + fi +fi +if ! command -v erl >/dev/null 2>&1; then + echo "error: erl not found. Gleam needs Erlang to run (target=erlang)." >&2 + echo " nix shell nixpkgs#beamPackages.erlang nixpkgs#gleam -c gleam run" >&2 + echo " # or: nix develop (from this flake)" >&2 + exit 1 +fi + # --- renderer --- if [[ -z "${PLUSHIE_BINARY_PATH:-}" ]]; then if [[ -x "$ROOT/bin/plushie-renderer" ]]; then @@ -21,7 +50,6 @@ if [[ "${1:-}" == "--packaged" ]]; then echo "No ./result/bin/global — run: nix build .#global --builders ''" >&2 exit 1 fi - # Packaged wrapper already sets LD_LIBRARY_PATH + env scrub. exec "$ROOT/result/bin/global" "$@" fi @@ -51,18 +79,6 @@ if command -v nix >/dev/null 2>&1; then fi # --- drop non-latin-1 env (nu prompts crash Plushie env FFI) --- -eval "$(python3 - <<'PY' -import os -for k, v in list(os.environ.items()): - try: - k.encode("latin-1") - v.encode("latin-1") - except UnicodeEncodeError: - print("unset -v " + repr(k)[1:-1] if False else "unset -v " + k) -PY -)" - -# safer: only unset valid bash names while IFS= read -r k; do unset -v "$k" 2>/dev/null || true done < <(python3 -c ' diff --git a/scripts/waydroid-gleam.sh b/scripts/waydroid-gleam.sh index 600a722..0f24420 100755 --- a/scripts/waydroid-gleam.sh +++ b/scripts/waydroid-gleam.sh @@ -165,22 +165,54 @@ EOF adb shell "rm -rf $HELLO_DEVICE && mkdir -p $HELLO_DEVICE && tar -C $HELLO_DEVICE -xzf /data/local/tmp/hello.tgz" } +# Env required for Termux-built BEAM under /data/local/tmp (not Termux home). +device_env() { + cat < /data/local/tmp/gleam-env.sh" < /data/local/tmp/bin/gleam <<'WRAP' +#!/system/bin/sh +. /data/local/tmp/gleam-env.sh +exec /data/local/tmp/termux-usr/bin/gleam \"\$@\" +WRAP +chmod 755 /data/local/tmp/bin/gleam /data/local/tmp/gleam-env.sh +" +} + run_smoke() { - local env_prefix="export PATH=$PREFIX_DEVICE/bin:/system/bin; export LD_LIBRARY_PATH=$PREFIX_DEVICE/lib; export HOME=/data/local/tmp; export ERL_ROOTDIR=$PREFIX_DEVICE/lib/erlang; export PREFIX=$PREFIX_DEVICE;" + install_wrappers + local env_prefix + env_prefix="$(device_env | tr '\n' ';')" local ver run ver="$(adb shell "$env_prefix $PREFIX_DEVICE/bin/gleam --version" | tr -d '\r')" echo "IN_WAYDROID: $ver" + # Full run with BEAM env — bare "gleam run" without ERL_ROOTDIR dies looking for Termux paths. run="$(adb shell "$env_prefix cd $HELLO_DEVICE && $PREFIX_DEVICE/bin/gleam run" | tr -d '\r')" echo "$run" echo "$ver" | grep -qi gleam || { echo "gleam version missing" >&2; exit 1; } - echo "$run" | grep -qx "$EXPECTED_LINE" || { - echo "expected stdout line '$EXPECTED_LINE', got:" >&2 + if ! echo "$run" | grep -q "$EXPECTED_LINE"; then + echo "expected stdout to contain '$EXPECTED_LINE', got:" >&2 echo "$run" >&2 exit 1 - } + fi if [[ -n "$EVIDENCE" ]]; then mkdir -p "$EVIDENCE" - # Tag evidence as captured from inside Waydroid (not host gleam) { echo "# captured via adb shell inside Waydroid" echo "# serial=${ANDROID_SERIAL:-unknown}" -- 2.51.2