| Write the build in babashka, and pin the babashka 34832a8 nandi 18d ago | 1 | #!/bin/sh |
| 2 | #_( |
| 3 | exec "$(dirname "$0")/../scripts/bb" "$0" "$@" |
| 4 | ) |
| 5 | |
| 6 | ;; Glue the two halves of the frq Android app into an APK. |
| 7 | ;; |
| Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago | 8 | ;; libvidya.so the C ABI on Rust/egui, out of the pinned release, and the |
| Write the build in babashka, and pin the babashka 34832a8 nandi 18d ago | 9 | ;; NativeActivity's own library (it holds android-activity's |
| 10 | ;; glue, so it owns the event loop) |
| Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago | 11 | ;; libjoltmoq.so the AV media plane — MoQ over QUIC, Opus, H.264, Camera2 |
| Write the build in babashka, and pin the babashka 34832a8 nandi 18d ago | 12 | ;; libjoltapp.so jolt-native's android/jolt_main.c plus frq's Jolt boot |
| 13 | ;; image, dlopened by the above |
| Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago | 14 | ;; libc++_shared.so the NDK's C++ runtime, which libjoltmoq names |
| 15 | ;; classes.dex two Java classes: a picture chooser answers through |
| 16 | ;; onActivityResult and a NativeActivity has nowhere to |
| 17 | ;; deliver that, and Camera2 has no C API worth the name |
| Write the build in babashka, and pin the babashka 34832a8 nandi 18d ago | 18 | ;; libssl.so OpenSSL, because the platform's own is not ours to load: an |
| 19 | ;; libcrypto.so app's linker namespace refuses /system/lib64/libssl.so, and |
| 20 | ;; without one there is no TLS at all on the phone |
| 21 | ;; |
| Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago | 22 | ;; Neither half is built here beyond that last link: the UI library and the glue |
| 23 | ;; come out of jolt-native's release, fetched by digest through the DotSlash |
| 24 | ;; pins in scripts/, and the boot image from build-jolt-boot.bb. Both native |
| 25 | ;; pieces are jolt-native's — only the boot image is frq's. |
| Write the build in babashka, and pin the babashka 34832a8 nandi 18d ago | 26 | ;; |
| 27 | ;; build-apk.bb [build|install|run|log] |
| 28 | (require '[babashka.classpath :as cp]) |
| 29 | (cp/add-classpath (str (babashka.fs/path (babashka.fs/parent *file*) ".." "scripts"))) |
| 30 | (require '[frq.paths :as paths] |
| 31 | '[babashka.fs :as fs] |
| 32 | '[babashka.process :as p]) |
| 33 | |
| 34 | (def root (str (fs/canonicalize (fs/path (fs/parent *file*) "..")))) |
| 35 | (def action (or (first *command-line-args*) "build")) |
| 36 | |
| 37 | (def android-home (paths/env "ANDROID_HOME" (str (fs/path (fs/home) ".local" "share" "android-sdk")))) |
| 38 | (def ndk-home (paths/env "ANDROID_NDK_HOME" (str (fs/path (fs/home) ".local" "share" "android-ndk-r29")))) |
| 39 | (def chez (paths/env "CHEZ_ANDROID" (str (fs/path (fs/home) ".cache" "vidya-chez-android")))) |
| 40 | (def openssl (paths/env "OPENSSL_ANDROID" (str (fs/path (fs/home) ".cache" "frq-openssl-android" "lib")))) |
| 41 | (def build (fs/path root "android" "build")) |
| 42 | (def jolt-build (fs/path build "jolt")) |
| 43 | (def stage (fs/path build "stage")) |
| 44 | (def tools (fs/path android-home "build-tools" "36.0.0")) |
| 45 | (def android-jar (fs/path android-home "platforms" "android-36" "android.jar")) |
| 46 | (def adb (paths/env "ADB" (str (fs/path android-home "platform-tools" "adb")))) |
| 47 | (def ndk-bin (fs/path ndk-home "toolchains" "llvm" "prebuilt" "linux-x86_64" "bin")) |
| 48 | (def package "uk.nandi.frq") |
| 49 | (def activity (str package "/.FrqActivity")) |
| 50 | (def api "28") |
| 51 | (def clang (str (fs/path ndk-bin (str "aarch64-linux-android" api "-clang")))) |
| 52 | (def arm-lib (fs/path stage "lib" "arm64-v8a")) |
| 53 | |
| 54 | (paths/require-paths! "Android tool" |
| 55 | [clang android-jar |
| 56 | (fs/path tools "aapt2") (fs/path tools "zipalign") |
| 57 | (fs/path tools "apksigner") (fs/path tools "d8") |
| 58 | (fs/path openssl "libssl.so") (fs/path openssl "libcrypto.so")]) |
| 59 | |
| 60 | ;; --- the UI half ----------------------------------------------------------- |
| Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago | 61 | ;; The release's own object, and the glue beside it, both by digest: this build |
| 62 | ;; compiles no Rust and needs no jolt-native checkout, exactly as the graph in |
| 63 | ;; android/BUCK needs none. |
| 64 | (def vidya-so (paths/dist root "libvidya-android")) |
| 65 | ;; The media plane, out of the same archive. The glue registers its symbols and |
| 66 | ;; includes its header, so this is not optional beside that jolt_main.c: link |
| 67 | ;; without it and --no-undefined says so. |
| 68 | (def joltmoq-so (paths/dist root "libjoltmoq-android")) |
| 69 | (def glue-c (paths/dist root "android-glue")) |
| 70 | (def glue-include (fs/path (fs/parent (fs/parent glue-c)) "include")) |
| 71 | (paths/require-paths! "library" [vidya-so joltmoq-so glue-c glue-include]) |
| Write the build in babashka, and pin the babashka 34832a8 nandi 18d ago | 72 | |
| 73 | ;; --- the Jolt half --------------------------------------------------------- |
| 74 | (p/shell (str (fs/path root "android" "build-jolt-boot.bb")) (str jolt-build)) |
| 75 | ;; The boot image travels as a blob in the object file's data section; the |
| 76 | ;; _binary_jolt_boot_{start,end} symbols jolt_main.c reads come from this. |
| 77 | (p/shell {:dir (str jolt-build)} |
| 78 | (str (fs/path ndk-bin "llvm-objcopy")) |
| 79 | "--input-target=binary" |
| 80 | "--output-target=elf64-littleaarch64" |
| 81 | "--binary-architecture=aarch64" |
| 82 | "jolt.boot" "jolt_boot.o") |
| 83 | |
| 84 | ;; --- the Java half --------------------------------------------------------- |
| Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago | 85 | ;; Two classes: the photo chooser's result has to land somewhere and native |
| 86 | ;; code is not somewhere, and Camera2 has no C API worth the name. d8 turns |
| 87 | ;; them into the classes.dex the runtime loads. |
| Write the build in babashka, and pin the babashka 34832a8 nandi 18d ago | 88 | (def java-build (fs/path build "java")) |
| 89 | (fs/delete-tree java-build) |
| 90 | (fs/create-dirs (fs/path java-build "classes")) |
| 91 | ;; android.jar on the class path is where every android.* type comes from; the |
| Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago | 92 | ;; JDK's own java.* is what is left, and neither class uses anything of it that |
| Write the build in babashka, and pin the babashka 34832a8 nandi 18d ago | 93 | ;; Android does not have. (`-bootclasspath` would be the stricter way to say |
| 94 | ;; that, and javac refuses it for a release this recent.) |
| Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago | 95 | ;; |
| 96 | ;; Every class under android/java, the way android/BUCK globs them: naming one |
| 97 | ;; here left CameraCapture out of the dex, and libjoltmoq looks that class up |
| 98 | ;; by name over JNI the moment a call starts the camera. |
| 99 | (apply p/shell "javac" "--release" "17" |
| 100 | "--class-path" (str android-jar) |
| 101 | "-d" (str (fs/path java-build "classes")) |
| 102 | (map str (fs/glob (fs/path root "android" "java") "**.java"))) |
| Write the build in babashka, and pin the babashka 34832a8 nandi 18d ago | 103 | (apply p/shell (str (fs/path tools "d8")) "--min-api" api "--output" (str java-build) |
| 104 | (map str (fs/glob (fs/path java-build "classes") "**.class"))) |
| 105 | |
| 106 | (fs/delete-tree stage) |
| 107 | (fs/create-dirs arm-lib) |
| 108 | (fs/copy (fs/path java-build "classes.dex") (fs/path stage "classes.dex")) |
| 109 | (fs/copy vidya-so (fs/path arm-lib "libvidya.so")) |
| Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago | 110 | (fs/copy joltmoq-so (fs/path arm-lib "libjoltmoq.so")) |
| 111 | ;; openh264 is C++ and asks to be linked against libc++_shared.so by name, so |
| 112 | ;; libjoltmoq carries it as a DT_NEEDED and the APK has to carry the object — |
| 113 | ;; an app's linker namespace will not hand out the platform's own. |
| 114 | (fs/copy (fs/path ndk-bin ".." "sysroot" "usr" "lib" "aarch64-linux-android" |
| 115 | "libc++_shared.so") |
| 116 | (fs/path arm-lib "libc++_shared.so")) |
| Write the build in babashka, and pin the babashka 34832a8 nandi 18d ago | 117 | ;; jolt.mvn-http dlopens these by name at first use; beside the app's own |
| 118 | ;; libraries is where an app's namespace will answer for that name. |
| 119 | (doseq [lib ["libssl.so" "libcrypto.so"]] |
| 120 | (fs/copy (fs/path openssl lib) (fs/path arm-lib lib))) |
| 121 | |
| 122 | (p/shell clang "-shared" "-fPIC" "-O2" |
| 123 | "-o" (str (fs/path arm-lib "libjoltapp.so")) |
| Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago | 124 | (str glue-c) |
| Write the build in babashka, and pin the babashka 34832a8 nandi 18d ago | 125 | (str (fs/path jolt-build "jolt_boot.o")) |
| 126 | (str "-I" jolt-build) |
| Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago | 127 | (str "-I" glue-include) |
| Write the build in babashka, and pin the babashka 34832a8 nandi 18d ago | 128 | (str "-L" arm-lib) |
| 129 | (str (fs/path chez "tarm64le" "boot" "tarm64le" "libkernel.a")) |
| 130 | (str (fs/path chez "lz4" "lib" "liblz4.a")) |
| Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago | 131 | "-lvidya" "-ljoltmoq" "-landroid" "-llog" "-lz" "-ldl" "-lm" |
| Write the build in babashka, and pin the babashka 34832a8 nandi 18d ago | 132 | "-Wl,--no-undefined") |
| 133 | |
| 134 | ;; --- the APK --------------------------------------------------------------- |
| 135 | (def unaligned (fs/path build "frq-unaligned.apk")) |
| 136 | (def aligned (fs/path build "frq-aligned.apk")) |
| 137 | (def apk (fs/path build "frq.apk")) |
| 138 | (run! fs/delete-if-exists [unaligned aligned apk]) |
| 139 | (p/shell (str (fs/path tools "aapt2")) "link" |
| 140 | "-o" (str unaligned) |
| 141 | "-I" (str android-jar) |
| 142 | "--manifest" (str (fs/path root "android" "AndroidManifest.xml")) |
| 143 | "--min-sdk-version" api |
| 144 | "--target-sdk-version" "36" |
| 145 | "--version-code" "1" |
| 146 | "--version-name" "0.1.0") |
| 147 | ;; Stored, not deflated: the loader maps these straight out of the APK. |
| 148 | (p/shell {:dir (str stage)} "zip" "-q" "-0" (str unaligned) |
| Take every jolt-native half from the release, and only from there a008d3b nandi 18d ago | 149 | "lib/arm64-v8a/libvidya.so" "lib/arm64-v8a/libjoltmoq.so" |
| 150 | "lib/arm64-v8a/libc++_shared.so" "lib/arm64-v8a/libjoltapp.so" |
| Write the build in babashka, and pin the babashka 34832a8 nandi 18d ago | 151 | "lib/arm64-v8a/libssl.so" "lib/arm64-v8a/libcrypto.so") |
| 152 | ;; The dex is read by the runtime rather than mapped, so it may as well deflate. |
| 153 | (p/shell {:dir (str stage)} "zip" "-q" (str unaligned) "classes.dex") |
| 154 | (p/shell (str (fs/path tools "zipalign")) "-f" "-p" "4" (str unaligned) (str aligned)) |
| 155 | |
| 156 | (def keystore (fs/path (fs/home) ".android" "debug.keystore")) |
| 157 | (when-not (fs/exists? keystore) |
| 158 | (fs/create-dirs (fs/parent keystore)) |
| 159 | (p/shell "keytool" "-genkeypair" "-v" |
| 160 | "-keystore" (str keystore) "-storepass" "android" "-keypass" "android" |
| 161 | "-alias" "androiddebugkey" "-keyalg" "RSA" "-keysize" "2048" |
| 162 | "-validity" "10000" |
| 163 | "-dname" "CN=Android Debug,O=Android,C=US")) |
| 164 | (p/shell (str (fs/path tools "apksigner")) "sign" |
| 165 | "--ks" (str keystore) "--ks-key-alias" "androiddebugkey" |
| 166 | "--ks-pass" "pass:android" "--key-pass" "pass:android" |
| 167 | "--out" (str apk) (str aligned)) |
| 168 | (p/shell {:out :string} (str (fs/path tools "apksigner")) "verify" (str apk)) |
| 169 | |
| 170 | (case action |
| 171 | "build" (println (str apk)) |
| 172 | "install" (p/shell adb "install" "-r" (str apk)) |
| 173 | "run" (do (p/shell adb "install" "-r" (str apk)) |
| 174 | (p/shell adb "shell" "am" "force-stop" package) |
| 175 | (p/shell adb "shell" "am" "start" "-n" activity)) |
| 176 | "log" (p/shell adb "logcat" "-s" "VidyaJolt" "Vidya") |
| 177 | (paths/die "usage: build-apk.bb [build|install|run|log]")) |