| A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago | 1 | #!/usr/bin/env bash |
| 2 | # Glue the two halves of the frq Android app into an APK. |
| 3 | # |
| 4 | # libvidya.so the C ABI on Rust/egui, cross-compiled by buck2, and the |
| 5 | # NativeActivity's own library (it holds android-activity's |
| 6 | # glue, so it owns the event loop) |
| Build the APK with buck2, from pins rather than from a second checkout a71ef8f nandi 19d ago | 7 | # libjoltapp.so jolt-native's android/jolt_main.c plus frq's Jolt boot |
| 8 | # image, dlopened by the above |
| Sign in and choose a picture on the phone 7b6e915 nandi 19d ago | 9 | # classes.dex one Java class, and only because a picture chooser answers |
| 10 | # through onActivityResult and a NativeActivity has nowhere to |
| 11 | # deliver that |
| Carry OpenSSL into the APK 7ef496a nandi 19d ago | 12 | # libssl.so OpenSSL, because the platform's own is not ours to load: an |
| 13 | # libcrypto.so app's linker namespace refuses /system/lib64/libssl.so, and |
| 14 | # without one there is no TLS at all on the phone |
| A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago | 15 | # |
| 16 | # Neither half is built here beyond that last link: the UI library comes from |
| Build the APK with buck2, from pins rather than from a second checkout a71ef8f nandi 19d ago | 17 | # jolt-native's `just ffi-android` and the boot image from build-jolt-boot.sh. |
| 18 | # Both native pieces are jolt-native's — only the boot image is frq's. |
| A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago | 19 | set -euo pipefail |
| 20 | |
| 21 | ROOT="$(cd "$(dirname "$0")/.." && pwd)" |
| Build the APK with buck2, from pins rather than from a second checkout a71ef8f nandi 19d ago | 22 | |
| 23 | # Where jolt-native is, answered the same way the justfile answers it: a |
| 24 | # sibling checkout wins, and otherwise it is the clone `just lib` leaves under |
| 25 | # .jolt-native. --git-common-dir rather than the working tree because this |
| 26 | # script runs from a worktree as readily as from the checkout, and in one of |
| 27 | # those "../jolt-native" is not a sibling of anything. |
| 28 | CHECKOUT="$(dirname "$(git -C "$ROOT" rev-parse --path-format=absolute --git-common-dir)")" |
| 29 | if [[ -z "${JOLT_NATIVE:-}" ]]; then |
| 30 | if [[ -d "$CHECKOUT/../jolt-native" ]]; then |
| 31 | JOLT_NATIVE="$(cd "$CHECKOUT/../jolt-native" && pwd)" |
| 32 | else |
| 33 | JOLT_NATIVE="$CHECKOUT/.jolt-native" |
| 34 | fi |
| 35 | fi |
| 36 | [[ -d "$JOLT_NATIVE" ]] || { |
| 37 | echo "no jolt-native at $JOLT_NATIVE — run \`just lib\` to clone it" >&2 |
| 38 | exit 1 |
| 39 | } |
| A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago | 40 | ANDROID_HOME="${ANDROID_HOME:-$HOME/.local/share/android-sdk}" |
| 41 | ANDROID_NDK_HOME="${ANDROID_NDK_HOME:-$HOME/.local/share/android-ndk-r29}" |
| 42 | CHEZ_ANDROID="${CHEZ_ANDROID:-$HOME/.cache/vidya-chez-android}" |
| Carry OpenSSL into the APK 7ef496a nandi 19d ago | 43 | OPENSSL_ANDROID="${OPENSSL_ANDROID:-$HOME/.cache/frq-openssl-android/lib}" |
| A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago | 44 | BUILD="$ROOT/android/build" |
| 45 | JOLT_BUILD="$BUILD/jolt" |
| 46 | STAGE="$BUILD/stage" |
| 47 | TOOLS="$ANDROID_HOME/build-tools/36.0.0" |
| 48 | ADB="${ADB:-$ANDROID_HOME/platform-tools/adb}" |
| 49 | NDK_BIN="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin" |
| 50 | PACKAGE="uk.nandi.frq" |
| Sign in and choose a picture on the phone 7b6e915 nandi 19d ago | 51 | ACTIVITY="$PACKAGE/.FrqActivity" |
| A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago | 52 | API=28 |
| 53 | |
| 54 | for path in \ |
| 55 | "$NDK_BIN/aarch64-linux-android$API-clang" \ |
| 56 | "$ANDROID_HOME/platforms/android-36/android.jar" \ |
| Carry OpenSSL into the APK 7ef496a nandi 19d ago | 57 | "$TOOLS/aapt2" "$TOOLS/zipalign" "$TOOLS/apksigner" "$TOOLS/d8" \ |
| 58 | "$OPENSSL_ANDROID/libssl.so" "$OPENSSL_ANDROID/libcrypto.so"; do |
| A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago | 59 | [[ -e "$path" ]] || { echo "missing Android tool: $path" >&2; exit 1; } |
| 60 | done |
| 61 | |
| 62 | # --- the UI half ------------------------------------------------------------ |
| Build the APK with buck2, from pins rather than from a second checkout a71ef8f nandi 19d ago | 63 | # buck2 fetches its own NDK for this, from the pin in jolt-native's |
| 64 | # scripts/android-ndk.dotslash, so the toolchain below is the only one that has |
| 65 | # to be installed by hand. |
| 66 | ( cd "$JOLT_NATIVE" && just ffi-android >&2 ) |
| 67 | VIDYA_SO="$JOLT_NATIVE/build/android/arm64-v8a/libvidya.so" |
| A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago | 68 | [[ -f "$VIDYA_SO" ]] || { echo "missing $VIDYA_SO" >&2; exit 1; } |
| 69 | |
| 70 | # --- the Jolt half ---------------------------------------------------------- |
| 71 | "$ROOT/android/build-jolt-boot.sh" "$JOLT_BUILD" |
| 72 | ( |
| 73 | cd "$JOLT_BUILD" |
| 74 | # The boot image travels as a blob in the object file's data section; the |
| 75 | # _binary_jolt_boot_{start,end} symbols jolt_main.c reads come from this. |
| 76 | "$NDK_BIN/llvm-objcopy" \ |
| 77 | --input-target=binary \ |
| 78 | --output-target=elf64-littleaarch64 \ |
| 79 | --binary-architecture=aarch64 \ |
| 80 | jolt.boot jolt_boot.o |
| 81 | ) |
| 82 | |
| Sign in and choose a picture on the phone 7b6e915 nandi 19d ago | 83 | # --- the Java half ---------------------------------------------------------- |
| 84 | # One class: the photo chooser's result has to land somewhere, and native code |
| 85 | # is not somewhere. d8 turns it into the classes.dex the runtime loads. |
| 86 | JAVA_BUILD="$BUILD/java" |
| 87 | rm -rf "$JAVA_BUILD" |
| 88 | mkdir -p "$JAVA_BUILD/classes" |
| 89 | # android.jar on the class path is where every android.* type comes from; the |
| 90 | # JDK's own java.* is what is left, and this class uses nothing of it that |
| 91 | # Android does not have. (`-bootclasspath` would be the stricter way to say |
| 92 | # that, and javac refuses it for a release this recent.) |
| 93 | javac --release 17 \ |
| 94 | --class-path "$ANDROID_HOME/platforms/android-36/android.jar" \ |
| 95 | -d "$JAVA_BUILD/classes" \ |
| 96 | "$ROOT/android/java/uk/nandi/frq/FrqActivity.java" |
| 97 | "$TOOLS/d8" --min-api $API --output "$JAVA_BUILD" \ |
| 98 | $(find "$JAVA_BUILD/classes" -name '*.class') |
| 99 | |
| A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago | 100 | rm -rf "$STAGE" |
| 101 | mkdir -p "$STAGE/lib/arm64-v8a" |
| Sign in and choose a picture on the phone 7b6e915 nandi 19d ago | 102 | cp "$JAVA_BUILD/classes.dex" "$STAGE/classes.dex" |
| A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago | 103 | cp "$VIDYA_SO" "$STAGE/lib/arm64-v8a/libvidya.so" |
| Carry OpenSSL into the APK 7ef496a nandi 19d ago | 104 | # jolt.mvn-http dlopens these by name at first use; beside the app's own |
| 105 | # libraries is where an app's namespace will answer for that name. |
| 106 | cp "$OPENSSL_ANDROID/libssl.so" "$OPENSSL_ANDROID/libcrypto.so" \ |
| 107 | "$STAGE/lib/arm64-v8a/" |
| A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago | 108 | |
| 109 | "$NDK_BIN/aarch64-linux-android$API-clang" \ |
| 110 | -shared -fPIC -O2 \ |
| 111 | -o "$STAGE/lib/arm64-v8a/libjoltapp.so" \ |
| Build the APK with buck2, from pins rather than from a second checkout a71ef8f nandi 19d ago | 112 | "$JOLT_NATIVE/android/jolt_main.c" \ |
| A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago | 113 | "$JOLT_BUILD/jolt_boot.o" \ |
| 114 | -I"$JOLT_BUILD" \ |
| Build the APK with buck2, from pins rather than from a second checkout a71ef8f nandi 19d ago | 115 | -I"$JOLT_NATIVE/crates/jolt-vidya/include" \ |
| A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago | 116 | -L"$STAGE/lib/arm64-v8a" \ |
| 117 | "$CHEZ_ANDROID/tarm64le/boot/tarm64le/libkernel.a" \ |
| 118 | "$CHEZ_ANDROID/lz4/lib/liblz4.a" \ |
| 119 | -lvidya -landroid -llog -lz -ldl -lm \ |
| 120 | -Wl,--no-undefined |
| 121 | |
| 122 | # --- the APK ---------------------------------------------------------------- |
| 123 | UNALIGNED="$BUILD/frq-unaligned.apk" |
| 124 | ALIGNED="$BUILD/frq-aligned.apk" |
| 125 | APK="$BUILD/frq.apk" |
| 126 | rm -f "$UNALIGNED" "$ALIGNED" "$APK" |
| 127 | "$TOOLS/aapt2" link \ |
| 128 | -o "$UNALIGNED" \ |
| 129 | -I "$ANDROID_HOME/platforms/android-36/android.jar" \ |
| 130 | --manifest "$ROOT/android/AndroidManifest.xml" \ |
| 131 | --min-sdk-version $API \ |
| 132 | --target-sdk-version 36 \ |
| 133 | --version-code 1 \ |
| 134 | --version-name 0.1.0 |
| 135 | # Stored, not deflated: the loader maps these straight out of the APK. |
| 136 | (cd "$STAGE" && zip -q -0 "$UNALIGNED" \ |
| Carry OpenSSL into the APK 7ef496a nandi 19d ago | 137 | lib/arm64-v8a/libvidya.so lib/arm64-v8a/libjoltapp.so \ |
| 138 | lib/arm64-v8a/libssl.so lib/arm64-v8a/libcrypto.so) |
| Sign in and choose a picture on the phone 7b6e915 nandi 19d ago | 139 | # The dex is read by the runtime rather than mapped, so it may as well deflate. |
| 140 | (cd "$STAGE" && zip -q "$UNALIGNED" classes.dex) |
| A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago | 141 | "$TOOLS/zipalign" -f -p 4 "$UNALIGNED" "$ALIGNED" |
| 142 | |
| 143 | KEYSTORE="$HOME/.android/debug.keystore" |
| 144 | if [[ ! -f "$KEYSTORE" ]]; then |
| 145 | mkdir -p "$(dirname "$KEYSTORE")" |
| 146 | keytool -genkeypair -v \ |
| 147 | -keystore "$KEYSTORE" -storepass android -keypass android \ |
| 148 | -alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 \ |
| 149 | -dname "CN=Android Debug,O=Android,C=US" |
| 150 | fi |
| 151 | "$TOOLS/apksigner" sign \ |
| 152 | --ks "$KEYSTORE" --ks-key-alias androiddebugkey \ |
| 153 | --ks-pass pass:android --key-pass pass:android \ |
| 154 | --out "$APK" "$ALIGNED" |
| 155 | "$TOOLS/apksigner" verify "$APK" >/dev/null |
| 156 | |
| 157 | case "${1:-build}" in |
| 158 | build) printf '%s\n' "$APK" ;; |
| 159 | install) "$ADB" install -r "$APK" ;; |
| 160 | run) |
| 161 | "$ADB" install -r "$APK" |
| 162 | "$ADB" shell am force-stop "$PACKAGE" |
| 163 | "$ADB" shell am start -n "$ACTIVITY" |
| 164 | ;; |
| 165 | log) "$ADB" logcat -s VidyaJolt Vidya ;; |
| 166 | *) |
| 167 | echo "usage: $0 [build|install|run|log]" >&2 |
| 168 | exit 2 |
| 169 | ;; |
| 170 | esac |