docs/ had 26 developer documents flat beside the manual, and the two audiences are very differently sized: most readers want the manual and the gesture reference, a few want the register, the designs and the measurements. The manual and gestures.md stay at the top; everything for someone changing the code moves to docs/dev/, and the two documents that name their own successors — the v0.1 milestone and the UI-refinement plan — go to docs/dev/archive/ rather than being deleted, since both are still cited. docs/README.md is the index, users first. Every reference follows: code comments, Cargo manifests, the workflows, the pre-commit hook, the bench and traceability tools (which locate the repo root by docs/dev/requirements.md now), packaging, the Docker READMEs, CLAUDE.md, CONTRIBUTING.md and the README. The matrix links one level deeper and is regenerated. Links out of the moved documents into the tree gain a level; a link checker over every Markdown file finds none broken.
115 lines
5.3 KiB
Bash
Executable File
115 lines
5.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Build DarkRoom into an installable APK, without Gradle.
|
|
#
|
|
# ./docker/android/package.sh # build + package, debug-signed
|
|
# ./docker/android/package.sh --install # ...and adb install to a device
|
|
#
|
|
# Gradle would add a second build system, a second dependency tree, and a
|
|
# second place for the toolchain versions to drift out of step with the
|
|
# Dockerfile. The tools it would have driven — javac, aapt2, d8, zipalign,
|
|
# apksigner — are in the image already and are enough on their own, because
|
|
# the app is Rust: android-activity's glue calls android_main directly, and
|
|
# the only Java in the APK is Slint's helper plus the handful of classes
|
|
# Android insists on constructing itself (see assemble-apk.sh's Java step).
|
|
set -euo pipefail
|
|
|
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO="$(cd "${HERE}/../.." && pwd)"
|
|
|
|
ABI="arm64-v8a"
|
|
RUST_TARGET="aarch64-linux-android"
|
|
PKG_DIR="${REPO}/apps/darkroom-android/android"
|
|
# The container writes here (see build.sh); the APK is assembled in the same
|
|
# place so both halves of the build agree on one output directory.
|
|
CACHE="${XDG_CACHE_HOME:-${HOME}/.cache}/darkroom-android"
|
|
# Under the cache's target/ rather than beside it: the container writes to
|
|
# /work/target-android/apk, and that is the directory bind-mounted here.
|
|
OUT="${CACHE}/target/apk"
|
|
APK="${OUT}/darkroom.apk"
|
|
|
|
# The version, taken from the workspace rather than restated here.
|
|
#
|
|
# `AndroidManifest.xml` deliberately declares none: a manifest that states a
|
|
# version is a second place for one to be wrong, and it was — the APK reported
|
|
# `versionName=null` and `versionCode=0` on the device while the binary inside
|
|
# it knew perfectly well what it was.
|
|
#
|
|
# `versionCode` must be a single increasing integer, which a semantic version
|
|
# is not, so it is packed: MAJOR*10000 + MINOR*100 + PATCH. That keeps the
|
|
# ordering Android needs (it refuses to install an APK whose code is lower than
|
|
# the installed one) and stays readable — 0.4.0 is 400. It allows 99 minors and
|
|
# 99 patches per major, which is a limit worth knowing about and a long way off.
|
|
VERSION_NAME="$(sed -n 's/^version = "\(.*\)"$/\1/p' "${REPO}/Cargo.toml" | head -1)"
|
|
if [[ -z "${VERSION_NAME}" ]]; then
|
|
echo "error: no version in Cargo.toml" >&2
|
|
exit 1
|
|
fi
|
|
VERSION_CODE="$(awk -F. '{ print $1 * 10000 + $2 * 100 + $3 }' <<< "${VERSION_NAME}")"
|
|
echo "==> version ${VERSION_NAME} (code ${VERSION_CODE})"
|
|
|
|
INSTALL=0
|
|
[[ "${1:-}" == "--install" ]] && INSTALL=1
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 1. Cross-compile the shared library.
|
|
# ---------------------------------------------------------------------------
|
|
echo "==> building libdarkroom.so (${ABI})"
|
|
"${HERE}/build.sh" cargo ndk -t "${ABI}" -o /work/target-android/jniLibs \
|
|
build --release -p darkroom-android
|
|
|
|
SO="${CACHE}/target/jniLibs/${ABI}/libdarkroom.so"
|
|
[[ -f "${SO}" ]] || { echo "error: ${SO} not built" >&2; exit 1; }
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. Assemble the APK inside the container, where the SDK lives.
|
|
#
|
|
# The assembly itself is assemble-apk.sh, which runs in the image and is shared
|
|
# with CI — see its header. Only the mount layout is decided here: the repo is
|
|
# at /work and the cache's target directory at /work/target-android, so every
|
|
# default in that script already points at the right place.
|
|
# ---------------------------------------------------------------------------
|
|
#
|
|
# Release signing, when asked for. assemble-apk.sh selects it by the presence
|
|
# of KEYSTORE_PASS (see its header), and the keystore has to be reachable from
|
|
# inside the container, so a host path in KEYSTORE is copied under the mounted
|
|
# target directory for the duration of the build and removed after. The
|
|
# passwords travel as environment, never as arguments -- docs/dev/android-signing.md
|
|
# has the incantation.
|
|
# ---------------------------------------------------------------------------
|
|
echo "==> packaging APK"
|
|
SIGNING_ENV=()
|
|
CONTAINER_KEYSTORE=""
|
|
if [[ -n "${KEYSTORE_PASS:-}" ]]; then
|
|
[[ -f "${KEYSTORE:-}" ]] || { echo "error: KEYSTORE_PASS is set but KEYSTORE is not a file" >&2; exit 1; }
|
|
install -m 600 "${KEYSTORE}" "${CACHE}/target/release.keystore"
|
|
CONTAINER_KEYSTORE="${CACHE}/target/release.keystore"
|
|
SIGNING_ENV=(
|
|
KEYSTORE=/work/target-android/release.keystore
|
|
KEYSTORE_PASS="${KEYSTORE_PASS}"
|
|
KEY_PASS="${KEY_PASS:-${KEYSTORE_PASS}}"
|
|
KEY_ALIAS="${KEY_ALIAS:?KEY_ALIAS is required when KEYSTORE_PASS is set}"
|
|
)
|
|
fi
|
|
"${HERE}/build.sh" env \
|
|
ABI="${ABI}" RUST_TARGET="${RUST_TARGET}" \
|
|
DARKROOM_DEBUGGABLE="${DARKROOM_DEBUGGABLE:-}" \
|
|
"${SIGNING_ENV[@]}" \
|
|
/work/docker/android/assemble-apk.sh
|
|
if [[ -n "${CONTAINER_KEYSTORE}" ]]; then
|
|
rm -f "${CONTAINER_KEYSTORE}"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. Install from the host.
|
|
#
|
|
# The container has adb but no device: build.sh mounts no USB and shares no
|
|
# network, so the host's already-authorised adb server is the shorter path.
|
|
# ---------------------------------------------------------------------------
|
|
if [[ "${INSTALL}" == "1" ]]; then
|
|
command -v adb >/dev/null || { echo "error: adb not on PATH" >&2; exit 1; }
|
|
echo "==> installing"
|
|
adb install -r "${APK}"
|
|
echo "==> launching"
|
|
adb shell am start -n paris.tourolle.darkroom/android.app.NativeActivity
|
|
fi
|