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

Build the APK with buck2, from pins rather than from a second checkout a71ef8f · on 719742dcbe6d54f9a46d2dd1d059f7a72286e5ba · nandi · 18d ago
build-apk.sh · 170 lines · 6.9 KBBash Blame HistoryRaw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#!/usr/bin/env bash
# Glue the two halves of the frq Android app into an APK.
#
#   libvidya.so    the C ABI on Rust/egui, cross-compiled by buck2, and the
#                  NativeActivity's own library (it holds android-activity's
#                  glue, so it owns the event loop)
#   libjoltapp.so  jolt-native's android/jolt_main.c plus frq's Jolt boot
#                  image, dlopened by the above
#   classes.dex    one Java class, and only because a picture chooser answers
#                  through onActivityResult and a NativeActivity has nowhere to
#                  deliver that
#   libssl.so      OpenSSL, because the platform's own is not ours to load: an
#   libcrypto.so   app's linker namespace refuses /system/lib64/libssl.so, and
#                  without one there is no TLS at all on the phone
#
# Neither half is built here beyond that last link: the UI library comes from
# jolt-native's `just ffi-android` and the boot image from build-jolt-boot.sh.
# Both native pieces are jolt-native's — only the boot image is frq's.
set -euo pipefail

ROOT="$(cd "$(dirname "$0")/.." && pwd)"

# Where jolt-native is, answered the same way the justfile answers it: a
# sibling checkout wins, and otherwise it is the clone `just lib` leaves under
# .jolt-native. --git-common-dir rather than the working tree because this
# script runs from a worktree as readily as from the checkout, and in one of
# those "../jolt-native" is not a sibling of anything.
CHECKOUT="$(dirname "$(git -C "$ROOT" rev-parse --path-format=absolute --git-common-dir)")"
if [[ -z "${JOLT_NATIVE:-}" ]]; then
  if [[ -d "$CHECKOUT/../jolt-native" ]]; then
    JOLT_NATIVE="$(cd "$CHECKOUT/../jolt-native" && pwd)"
  else
    JOLT_NATIVE="$CHECKOUT/.jolt-native"
  fi
fi
[[ -d "$JOLT_NATIVE" ]] || {
  echo "no jolt-native at $JOLT_NATIVE — run \`just lib\` to clone it" >&2
  exit 1
}
ANDROID_HOME="${ANDROID_HOME:-$HOME/.local/share/android-sdk}"
ANDROID_NDK_HOME="${ANDROID_NDK_HOME:-$HOME/.local/share/android-ndk-r29}"
CHEZ_ANDROID="${CHEZ_ANDROID:-$HOME/.cache/vidya-chez-android}"
OPENSSL_ANDROID="${OPENSSL_ANDROID:-$HOME/.cache/frq-openssl-android/lib}"
BUILD="$ROOT/android/build"
JOLT_BUILD="$BUILD/jolt"
STAGE="$BUILD/stage"
TOOLS="$ANDROID_HOME/build-tools/36.0.0"
ADB="${ADB:-$ANDROID_HOME/platform-tools/adb}"
NDK_BIN="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin"
PACKAGE="uk.nandi.frq"
ACTIVITY="$PACKAGE/.FrqActivity"
API=28

for path in \
  "$NDK_BIN/aarch64-linux-android$API-clang" \
  "$ANDROID_HOME/platforms/android-36/android.jar" \
  "$TOOLS/aapt2" "$TOOLS/zipalign" "$TOOLS/apksigner" "$TOOLS/d8" \
  "$OPENSSL_ANDROID/libssl.so" "$OPENSSL_ANDROID/libcrypto.so"; do
  [[ -e "$path" ]] || { echo "missing Android tool: $path" >&2; exit 1; }
done

# --- the UI half ------------------------------------------------------------
# buck2 fetches its own NDK for this, from the pin in jolt-native's
# scripts/android-ndk.dotslash, so the toolchain below is the only one that has
# to be installed by hand.
( cd "$JOLT_NATIVE" && just ffi-android >&2 )
VIDYA_SO="$JOLT_NATIVE/build/android/arm64-v8a/libvidya.so"
[[ -f "$VIDYA_SO" ]] || { echo "missing $VIDYA_SO" >&2; exit 1; }

# --- the Jolt half ----------------------------------------------------------
"$ROOT/android/build-jolt-boot.sh" "$JOLT_BUILD"
(
  cd "$JOLT_BUILD"
  # The boot image travels as a blob in the object file's data section; the
  # _binary_jolt_boot_{start,end} symbols jolt_main.c reads come from this.
  "$NDK_BIN/llvm-objcopy" \
    --input-target=binary \
    --output-target=elf64-littleaarch64 \
    --binary-architecture=aarch64 \
    jolt.boot jolt_boot.o
)

# --- the Java half ----------------------------------------------------------
# One class: the photo chooser's result has to land somewhere, and native code
# is not somewhere. d8 turns it into the classes.dex the runtime loads.
JAVA_BUILD="$BUILD/java"
rm -rf "$JAVA_BUILD"
mkdir -p "$JAVA_BUILD/classes"
# android.jar on the class path is where every android.* type comes from; the
# JDK's own java.* is what is left, and this class uses nothing of it that
# Android does not have. (`-bootclasspath` would be the stricter way to say
# that, and javac refuses it for a release this recent.)
javac --release 17 \
  --class-path "$ANDROID_HOME/platforms/android-36/android.jar" \
  -d "$JAVA_BUILD/classes" \
  "$ROOT/android/java/uk/nandi/frq/FrqActivity.java"
"$TOOLS/d8" --min-api $API --output "$JAVA_BUILD" \
  $(find "$JAVA_BUILD/classes" -name '*.class')

rm -rf "$STAGE"
mkdir -p "$STAGE/lib/arm64-v8a"
cp "$JAVA_BUILD/classes.dex" "$STAGE/classes.dex"
cp "$VIDYA_SO" "$STAGE/lib/arm64-v8a/libvidya.so"
# jolt.mvn-http dlopens these by name at first use; beside the app's own
# libraries is where an app's namespace will answer for that name.
cp "$OPENSSL_ANDROID/libssl.so" "$OPENSSL_ANDROID/libcrypto.so" \
  "$STAGE/lib/arm64-v8a/"

"$NDK_BIN/aarch64-linux-android$API-clang" \
  -shared -fPIC -O2 \
  -o "$STAGE/lib/arm64-v8a/libjoltapp.so" \
  "$JOLT_NATIVE/android/jolt_main.c" \
  "$JOLT_BUILD/jolt_boot.o" \
  -I"$JOLT_BUILD" \
  -I"$JOLT_NATIVE/crates/jolt-vidya/include" \
  -L"$STAGE/lib/arm64-v8a" \
  "$CHEZ_ANDROID/tarm64le/boot/tarm64le/libkernel.a" \
  "$CHEZ_ANDROID/lz4/lib/liblz4.a" \
  -lvidya -landroid -llog -lz -ldl -lm \
  -Wl,--no-undefined

# --- the APK ----------------------------------------------------------------
UNALIGNED="$BUILD/frq-unaligned.apk"
ALIGNED="$BUILD/frq-aligned.apk"
APK="$BUILD/frq.apk"
rm -f "$UNALIGNED" "$ALIGNED" "$APK"
"$TOOLS/aapt2" link \
  -o "$UNALIGNED" \
  -I "$ANDROID_HOME/platforms/android-36/android.jar" \
  --manifest "$ROOT/android/AndroidManifest.xml" \
  --min-sdk-version $API \
  --target-sdk-version 36 \
  --version-code 1 \
  --version-name 0.1.0
# Stored, not deflated: the loader maps these straight out of the APK.
(cd "$STAGE" && zip -q -0 "$UNALIGNED" \
  lib/arm64-v8a/libvidya.so lib/arm64-v8a/libjoltapp.so \
  lib/arm64-v8a/libssl.so lib/arm64-v8a/libcrypto.so)
# The dex is read by the runtime rather than mapped, so it may as well deflate.
(cd "$STAGE" && zip -q "$UNALIGNED" classes.dex)
"$TOOLS/zipalign" -f -p 4 "$UNALIGNED" "$ALIGNED"

KEYSTORE="$HOME/.android/debug.keystore"
if [[ ! -f "$KEYSTORE" ]]; then
  mkdir -p "$(dirname "$KEYSTORE")"
  keytool -genkeypair -v \
    -keystore "$KEYSTORE" -storepass android -keypass android \
    -alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 \
    -dname "CN=Android Debug,O=Android,C=US"
fi
"$TOOLS/apksigner" sign \
  --ks "$KEYSTORE" --ks-key-alias androiddebugkey \
  --ks-pass pass:android --key-pass pass:android \
  --out "$APK" "$ALIGNED"
"$TOOLS/apksigner" verify "$APK" >/dev/null

case "${1:-build}" in
  build)   printf '%s\n' "$APK" ;;
  install) "$ADB" install -r "$APK" ;;
  run)
    "$ADB" install -r "$APK"
    "$ADB" shell am force-stop "$PACKAGE"
    "$ADB" shell am start -n "$ACTIVITY"
    ;;
  log)     "$ADB" logcat -s VidyaJolt Vidya ;;
  *)
    echo "usage: $0 [build|install|run|log]" >&2
    exit 2
    ;;
esac