nandi/frqpublic Fork 0
9cc5d9980472e2eee5e25ee46663aa0cf4eb1cbe
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-apk.bb · 163 lines · 8.2 KBBlitzBasic Blame HistoryRaw
Write the build in babashka, and pin the babashka 34832a8 nandi 18d ago1#!/bin/sh
2#_(
3exec "$(dirname "$0")/../scripts/bb" "$0" "$@"
4)
5
6;; Glue the two halves of the frq Android app into an APK.
7;;
8;; libvidya.so the C ABI on Rust/egui, cross-compiled by buck2, and the
9;; NativeActivity's own library (it holds android-activity's
10;; glue, so it owns the event loop)
11;; libjoltapp.so jolt-native's android/jolt_main.c plus frq's Jolt boot
12;; image, dlopened by the above
13;; classes.dex one Java class, and only because a picture chooser answers
14;; through onActivityResult and a NativeActivity has nowhere
15;; to deliver that
16;; libssl.so OpenSSL, because the platform's own is not ours to load: an
17;; libcrypto.so app's linker namespace refuses /system/lib64/libssl.so, and
18;; without one there is no TLS at all on the phone
19;;
20;; Neither half is built here beyond that last link: the UI library comes from
21;; jolt-native's `just ffi-android` and the boot image from build-jolt-boot.bb.
22;; Both native pieces are jolt-native's — only the boot image is frq's.
23;;
24;; build-apk.bb [build|install|run|log]
25(require '[babashka.classpath :as cp])
26(cp/add-classpath (str (babashka.fs/path (babashka.fs/parent *file*) ".." "scripts")))
27(require '[frq.paths :as paths]
28 '[babashka.fs :as fs]
29 '[babashka.process :as p])
30
31(def root (str (fs/canonicalize (fs/path (fs/parent *file*) ".."))))
32(def action (or (first *command-line-args*) "build"))
33
34;; Where jolt-native is, answered the way the justfile answers it: a sibling
35;; checkout wins, and otherwise it is the clone `just lib` leaves under
36;; .jolt-native.
37(def jolt-native (paths/jolt-native root))
38(when-not (fs/directory? jolt-native)
39 (paths/die (str "no jolt-native at " jolt-native " — run `just lib` to clone it")))
40
41(def android-home (paths/env "ANDROID_HOME" (str (fs/path (fs/home) ".local" "share" "android-sdk"))))
42(def ndk-home (paths/env "ANDROID_NDK_HOME" (str (fs/path (fs/home) ".local" "share" "android-ndk-r29"))))
43(def chez (paths/env "CHEZ_ANDROID" (str (fs/path (fs/home) ".cache" "vidya-chez-android"))))
44(def openssl (paths/env "OPENSSL_ANDROID" (str (fs/path (fs/home) ".cache" "frq-openssl-android" "lib"))))
45(def build (fs/path root "android" "build"))
46(def jolt-build (fs/path build "jolt"))
47(def stage (fs/path build "stage"))
48(def tools (fs/path android-home "build-tools" "36.0.0"))
49(def android-jar (fs/path android-home "platforms" "android-36" "android.jar"))
50(def adb (paths/env "ADB" (str (fs/path android-home "platform-tools" "adb"))))
51(def ndk-bin (fs/path ndk-home "toolchains" "llvm" "prebuilt" "linux-x86_64" "bin"))
52(def package "uk.nandi.frq")
53(def activity (str package "/.FrqActivity"))
54(def api "28")
55(def clang (str (fs/path ndk-bin (str "aarch64-linux-android" api "-clang"))))
56(def arm-lib (fs/path stage "lib" "arm64-v8a"))
57
58(paths/require-paths! "Android tool"
59 [clang android-jar
60 (fs/path tools "aapt2") (fs/path tools "zipalign")
61 (fs/path tools "apksigner") (fs/path tools "d8")
62 (fs/path openssl "libssl.so") (fs/path openssl "libcrypto.so")])
63
64;; --- the UI half -----------------------------------------------------------
65;; buck2 fetches its own NDK for this, from the pin in jolt-native's
66;; scripts/android-ndk.dotslash, so the toolchain above is the only one that
67;; has to be installed by hand.
68(p/shell {:dir jolt-native :out *err*} "just" "ffi-android")
69(def vidya-so (fs/path jolt-native "build" "android" "arm64-v8a" "libvidya.so"))
70(paths/require-paths! "library" [vidya-so])
71
72;; --- the Jolt half ---------------------------------------------------------
73(p/shell (str (fs/path root "android" "build-jolt-boot.bb")) (str 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(p/shell {:dir (str jolt-build)}
77 (str (fs/path ndk-bin "llvm-objcopy"))
78 "--input-target=binary"
79 "--output-target=elf64-littleaarch64"
80 "--binary-architecture=aarch64"
81 "jolt.boot" "jolt_boot.o")
82
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(def java-build (fs/path build "java"))
87(fs/delete-tree java-build)
88(fs/create-dirs (fs/path 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(p/shell "javac" "--release" "17"
94 "--class-path" (str android-jar)
95 "-d" (str (fs/path java-build "classes"))
96 (str (fs/path root "android" "java" "uk" "nandi" "frq" "FrqActivity.java")))
97(apply p/shell (str (fs/path tools "d8")) "--min-api" api "--output" (str java-build)
98 (map str (fs/glob (fs/path java-build "classes") "**.class")))
99
100(fs/delete-tree stage)
101(fs/create-dirs arm-lib)
102(fs/copy (fs/path java-build "classes.dex") (fs/path stage "classes.dex"))
103(fs/copy vidya-so (fs/path arm-lib "libvidya.so"))
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(doseq [lib ["libssl.so" "libcrypto.so"]]
107 (fs/copy (fs/path openssl lib) (fs/path arm-lib lib)))
108
109(p/shell clang "-shared" "-fPIC" "-O2"
110 "-o" (str (fs/path arm-lib "libjoltapp.so"))
111 (str (fs/path jolt-native "android" "jolt_main.c"))
112 (str (fs/path jolt-build "jolt_boot.o"))
113 (str "-I" jolt-build)
114 (str "-I" (fs/path jolt-native "crates" "jolt-vidya" "include"))
115 (str "-L" arm-lib)
116 (str (fs/path chez "tarm64le" "boot" "tarm64le" "libkernel.a"))
117 (str (fs/path chez "lz4" "lib" "liblz4.a"))
118 "-lvidya" "-landroid" "-llog" "-lz" "-ldl" "-lm"
119 "-Wl,--no-undefined")
120
121;; --- the APK ---------------------------------------------------------------
122(def unaligned (fs/path build "frq-unaligned.apk"))
123(def aligned (fs/path build "frq-aligned.apk"))
124(def apk (fs/path build "frq.apk"))
125(run! fs/delete-if-exists [unaligned aligned apk])
126(p/shell (str (fs/path tools "aapt2")) "link"
127 "-o" (str unaligned)
128 "-I" (str android-jar)
129 "--manifest" (str (fs/path root "android" "AndroidManifest.xml"))
130 "--min-sdk-version" api
131 "--target-sdk-version" "36"
132 "--version-code" "1"
133 "--version-name" "0.1.0")
134;; Stored, not deflated: the loader maps these straight out of the APK.
135(p/shell {:dir (str stage)} "zip" "-q" "-0" (str unaligned)
136 "lib/arm64-v8a/libvidya.so" "lib/arm64-v8a/libjoltapp.so"
137 "lib/arm64-v8a/libssl.so" "lib/arm64-v8a/libcrypto.so")
138;; The dex is read by the runtime rather than mapped, so it may as well deflate.
139(p/shell {:dir (str stage)} "zip" "-q" (str unaligned) "classes.dex")
140(p/shell (str (fs/path tools "zipalign")) "-f" "-p" "4" (str unaligned) (str aligned))
141
142(def keystore (fs/path (fs/home) ".android" "debug.keystore"))
143(when-not (fs/exists? keystore)
144 (fs/create-dirs (fs/parent keystore))
145 (p/shell "keytool" "-genkeypair" "-v"
146 "-keystore" (str keystore) "-storepass" "android" "-keypass" "android"
147 "-alias" "androiddebugkey" "-keyalg" "RSA" "-keysize" "2048"
148 "-validity" "10000"
149 "-dname" "CN=Android Debug,O=Android,C=US"))
150(p/shell (str (fs/path tools "apksigner")) "sign"
151 "--ks" (str keystore) "--ks-key-alias" "androiddebugkey"
152 "--ks-pass" "pass:android" "--key-pass" "pass:android"
153 "--out" (str apk) (str aligned))
154(p/shell {:out :string} (str (fs/path tools "apksigner")) "verify" (str apk))
155
156(case action
157 "build" (println (str apk))
158 "install" (p/shell adb "install" "-r" (str apk))
159 "run" (do (p/shell adb "install" "-r" (str apk))
160 (p/shell adb "shell" "am" "force-stop" package)
161 (p/shell adb "shell" "am" "start" "-n" activity))
162 "log" (p/shell adb "logcat" "-s" "VidyaJolt" "Vidya")
163 (paths/die "usage: build-apk.bb [build|install|run|log]"))