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

Carry OpenSSL into the APK 7ef496a · on ddbf5e1966a7d04e46e03c3b273f5032ab015d4f · nandi · 19d ago
build-apk.sh · 151 lines · 6.0 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
#!/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  vidya'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
# vidya's `just ffi-android` and the boot image from build-jolt-boot.sh. Both
# native pieces are vidya's — only the boot image is frq's.
set -euo pipefail

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
VIDYA="${VIDYA:-$(cd "$ROOT/../vidya" && pwd)}"
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 ------------------------------------------------------------
( cd "$VIDYA" && just ffi-android >&2 )
VIDYA_SO="$VIDYA/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" \
  "$VIDYA/android/jolt_main.c" \
  "$JOLT_BUILD/jolt_boot.o" \
  -I"$JOLT_BUILD" \
  -I"$VIDYA/raylib/include" \
  -I"$VIDYA/ffi/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