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

Pin the media plane, now that there is a release to pin 03d5a6b · on 03d5a6b415596ef67c4a9f68cfd4476e3ebd0afb · nandi · 18d ago
BUCK · 239 lines · 11.9 KBPython 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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# The APK, as a graph rather than a script.
#
# Every step the old build-apk script ran in sequence is a target here, so a change
# to one screen rebuilds the boot image and repackages, and touches neither the
# Rust nor the Java. The two native halves come from jolt-native — libvidya out
# of its buck2 graph, the glue compiled by the NDK its scripts/ pin — and the
# rest is this repo's.
#
# The machine-specific paths are read from .buckconfig.local, which the `buck`
# recipe in the justfile writes. Nothing here is found by looking around the
# machine; if a path is missing the recipe says which.
load(":defs.bzl", "glue", "libjoltmoq", "libvidya")

_JOLT_NATIVE = read_root_config("frq", "jolt_native", "")
_ANDROID_HOME = read_root_config("frq", "android_home", "")
_CHEZ = read_root_config("frq", "chez_android", "")
_OPENSSL = read_root_config("frq", "openssl_android", "")

_API = "28"
_TOOLS = _ANDROID_HOME + "/build-tools/36.0.0"
_ANDROID_JAR = _ANDROID_HOME + "/platforms/android-36/android.jar"
# `$(...)` in a genrule cmd is a buck macro, not the shell's substitution, so
# everything this file runs at command time uses backticks.
# This repo's own, not the other checkout's: an APK build needs a clang and an
# llvm-objcopy whether or not jolt-native is on the machine. Both resolve the
# NDK through scripts/android-ndk.dotslash, downloading it once.
_NDK_BIN = "ndk=`$(location //scripts:android-ndk-bin)`"
_CC = "$(location //scripts:android-cc)"

# An absolute path for $OUT, so a command may cd without losing it. buck has
# already made the parent directory. Spelled out step by step because backticks
# do not nest.
# Empty when a checkout answers for it, so the script's own lookup wins.
# The archive's top directory is what strip_prefix took off, so `src` is
# directly inside it. Absolute, because the script is handed this and does its
# own directory changing.
_GLIMMER_VIDYA = "" if read_root_config("frq", "libvidya", "pinned") == "checkout" else \
                 "GLIMMER_VIDYA=`realpath $(location toolchains//dist:glimmer-vidya)/src` "

# jolt_main.c and the ABI's headers, as targets — see defs.bzl for which of
# the two sources answers, and why neither may be a bare path.
_GLUE_C = "`realpath $(location :glue-c)`"
_GLUE_INCLUDE = "`realpath $(location :glue-include)`"

_ABS_OUT = 'd=`dirname "$OUT"` && b=`basename "$OUT"` && d=`cd "$d" && pwd` && out="$d/$b"'

# Somewhere to assemble in. Removed by the command that made it, on success;
# buck's own scratch space is not this.
_TMP = 'tmp=`mktemp -d`'

# A babashka script, run in place: it loads scripts/frq/paths.clj beside it and
# resolves its own interpreter through scripts/bb, the DotSlash pin. Referenced
# rather than copied for exactly that reason — the tree around it is part of it.
export_file(
    name = "build-jolt-boot.bb",
    mode = "reference",
)

# --- the UI half ------------------------------------------------------------
# Two ways in, and both make the library's bytes an input rather than a
# command buck would cache forever:
#
#   pinned    the .so out of jolt-native's release, fetched by digest. What a
#             build gets by default, and what makes an APK buildable with no
#             jolt-native checkout and no NDK anywhere on the machine.
#   checkout  the .so a sibling jolt-native just built, staged into this cell
#             by the `buck` recipe. Anyone working on both repos at once
#             builds what they are editing.
#
# The recipe decides which, by whether that checkout exists, and says so here.
libvidya(name = "libvidya")

# The media plane, on the same terms and from the same release.
libjoltmoq(name = "libjoltmoq")

glue(c_name = "glue-c", include_name = "glue-include")

# The C++ runtime, out of the same NDK the glue is compiled with.
#
# openh264 is C++, and its build script asks to be linked against
# `libc++_shared.so` by name — so libjoltmoq carries that as a DT_NEEDED. An
# app's linker namespace will not hand out the platform's own copy (there is no
# stable one to hand out), so the APK has to carry it, exactly as it carries
# OpenSSL below and for the same reason.
#
# Only when the media plane is packaged: nothing else here is C++.
genrule(
    name = "libcxx",
    out = "libc++_shared.so",
    cmd = _ABS_OUT + " && " + _NDK_BIN + " && " +
          "cp \"$ndk\"/../sysroot/usr/lib/aarch64-linux-android/libc++_shared.so \"$out\"",
)

# --- the Jolt half ----------------------------------------------------------
# The boot image: frq's Scheme, cross-compiled to arm64 by Chez. The sources
# are an input so that editing one rebuilds this; the compile itself reads them
# through the deps.edn the script writes.
# The sources here are frq's own. The other two roots the image is compiled
# from — glimmer, out of the jolt cache, and glimmer-vidya, out of jolt-native
# — are outside this cell, and so are Chez's cross boot files and the pinned
# jolt itself. None of them can be an input, so the `buck` recipe hashes them
# and writes the digest below; naming it in the command is what puts them in
# this action's identity.
# jolt reaches this action as a manifest and a fetcher rather than as a
# program: both are inputs, so nothing here depends on what is installed where
# the compile runs, and a remote worker resolves the same pin — by the same
# digest — from the same place this machine would.
genrule(
    name = "jolt-boot",
    out = "boot",
    srcs = ["//:jolt-sources"],
    cmd = "# sources outside this cell: " + read_root_config("frq", "boot_stamp", "unknown") + "\n" +
          "DOTSLASH=$(location toolchains//dist:dotslash)/dotslash " +
          "JOLT_MANIFEST=$(location //scripts:jolt) " +
          # The one source root the script cannot find for itself when there
          # is no jolt-native checkout. An input, so a new release recompiles
          # the image.
          _GLIMMER_VIDYA +
          "$(location :build-jolt-boot.bb) \"$OUT\" >&2",
)

# The image travels as a blob in an object file's data section; the
# _binary_jolt_boot_{start,end} symbols jolt_main.c reads are named after the
# input *path*, which is why this copies the file somewhere it can be called
# exactly `jolt.boot` before converting it.
genrule(
    name = "jolt-boot-obj",
    out = "jolt_boot.o",
    cmd = _ABS_OUT + " && " +
          _TMP + " && " + _NDK_BIN + " && " +
          "cp $(location :jolt-boot)/jolt.boot \"$tmp/jolt.boot\" && " +
          "( cd \"$tmp\" && \"$ndk\"/llvm-objcopy " +
          "--input-target=binary --output-target=elf64-littleaarch64 " +
          "--binary-architecture=aarch64 jolt.boot jolt_boot.o ) && " +
          "cp \"$tmp/jolt_boot.o\" \"$out\" && rm -rf \"$tmp\"",
)

# The glue: jolt-native's android/jolt_main.c over the boot image, linked
# against libvidya by name. --no-undefined is what makes a symbol the Scheme
# side registers but the ABI no longer exports a build failure here rather than
# a crash on the phone.
genrule(
    name = "libjoltapp",
    out = "libjoltapp.so",
    cmd = _ABS_OUT + " && lib=`mktemp -d` && " +
          "cp $(location :libvidya) \"$lib/libvidya.so\" && " +
          "cp $(location :libjoltmoq) \"$lib/libjoltmoq.so\" && " +
          _CC + " -shared -fPIC -O2 -o \"$out\" " +
          _GLUE_C + " " +
          "$(location :jolt-boot-obj) " +
          "-I$(location :jolt-boot) " +
          "-I" + _GLUE_INCLUDE + " " +
          "-L\"$lib\" " +
          _CHEZ + "/tarm64le/boot/tarm64le/libkernel.a " +
          _CHEZ + "/lz4/lib/liblz4.a " +
          "-lvidya -ljoltmoq -landroid -llog -lz -ldl -lm -Wl,--no-undefined && " +
          "rm -rf \"$lib\"",
)

# --- the Java half ----------------------------------------------------------
# Two classes: the photo chooser's result has to land somewhere and native code
# is not somewhere, and Camera2 has no C API worth the name — CameraCapture
# opens the camera in Java and pushes NV12 planes down to libjoltmoq over JNI.
# android.jar on the class path is where every android.* type comes from; the
# JDK's own java.* is what is left.
genrule(
    name = "classes-dex",
    out = "classes.dex",
    srcs = glob(["java/**/*.java"]),
    cmd = _ABS_OUT + " && " + _TMP + " && " +
          "javac --release 17 --class-path " + _ANDROID_JAR + " -d \"$tmp/classes\" $SRCS && " +
          "classes=`find \"$tmp/classes\" -name '*.class'` && " +
          _TOOLS + "/d8 --min-api " + _API + " --output \"$tmp\" $classes && " +
          "cp \"$tmp/classes.dex\" \"$out\" && rm -rf \"$tmp\"",
)

# --- the package ------------------------------------------------------------
# The libraries are stored rather than deflated: the loader maps them straight
# out of the APK. The dex is read rather than mapped, so it may as well
# compress. OpenSSL travels with the app because the platform's own is not ours
# to load — an app's linker namespace refuses /system/lib64/libssl.so, and
# without one there is no TLS on the phone at all.
genrule(
    name = "apk-unsigned",
    out = "frq-unsigned.apk",
    srcs = ["AndroidManifest.xml"],
    cmd = _ABS_OUT + " && stage=`mktemp -d` && " +
          "mkdir -p \"$stage/lib/arm64-v8a\" && " +
          "cp $(location :libvidya) \"$stage/lib/arm64-v8a/libvidya.so\" && " +
          "cp $(location :libjoltapp) \"$stage/lib/arm64-v8a/libjoltapp.so\" && " +
          "cp $(location :libjoltmoq) \"$stage/lib/arm64-v8a/libjoltmoq.so\" && " +
          "cp $(location :libcxx) \"$stage/lib/arm64-v8a/libc++_shared.so\" && " +
          "cp " + _OPENSSL + "/libssl.so " + _OPENSSL + "/libcrypto.so \"$stage/lib/arm64-v8a/\" && " +
          "cp $(location :classes-dex) \"$stage/classes.dex\" && " +
          _TOOLS + "/aapt2 link -o \"$out\" -I " + _ANDROID_JAR + " " +
          "--manifest $SRCS --min-sdk-version " + _API + " --target-sdk-version 36 " +
          "--version-code 1 --version-name 0.1.0 >&2 && " +
          "( cd \"$stage\" && zip -q -0 \"$out\" lib/arm64-v8a/libvidya.so " +
          "lib/arm64-v8a/libjoltmoq.so lib/arm64-v8a/libc++_shared.so " +
          "lib/arm64-v8a/libjoltapp.so lib/arm64-v8a/libssl.so lib/arm64-v8a/libcrypto.so && " +
          "zip -q \"$out\" classes.dex ) && rm -rf \"$stage\"",
)

# Aligned and signed with the debug key, which is generated on first use the
# way the SDK's own tools generate it.
genrule(
    name = "apk",
    out = "frq.apk",
    cmd = _ABS_OUT + " && " + _TMP + " && " +
          "ks=\"$HOME/.android/debug.keystore\" && " +
          "if [ ! -f \"$ks\" ]; then mkdir -p \"$HOME/.android\" && " +
          "keytool -genkeypair -v -keystore \"$ks\" -storepass android -keypass android " +
          "-alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 " +
          "-dname 'CN=Android Debug,O=Android,C=US' >&2; fi && " +
          _TOOLS + "/zipalign -f -p 4 $(location :apk-unsigned) \"$tmp/aligned.apk\" && " +
          _TOOLS + "/apksigner sign --ks \"$ks\" --ks-key-alias androiddebugkey " +
          "--ks-pass pass:android --key-pass pass:android --out \"$out\" \"$tmp/aligned.apk\" && " +
          _TOOLS + "/apksigner verify \"$out\" >/dev/null && rm -rf \"$tmp\"",
    visibility = ["PUBLIC"],
)

# Does the pin resolve where the action runs, and does what it resolves to run
# there? Nothing depends on this; it is here to be asked by hand.
#
#   buck2 build //android:jolt-fetch-check --remote-only --materializations=all \
#     -c build.execution_platforms=root//platforms:remote -c "parser...->root//platforms:remote"
#
# It is what found the glibc floor: jolt needs 2.38, and the rbe-ubuntu22-04
# image this platform used to name ships 2.35, so the fetch succeeded and the
# binary would not start. The answer was the newer image, not a different pin.
genrule(
    name = "jolt-fetch-check",
    out = "report",
    cmd = "jolt=`$(location toolchains//dist:dotslash)/dotslash -- fetch $(location //scripts:jolt)`; " +
          "{ echo \"fetched: $jolt\"; " +
          "cat /etc/os-release 2>/dev/null | head -1 || true; " +
          "ldd --version 2>&1 | head -1 || true; " +
          "echo '--- run ---'; \"$jolt\" --version; echo \"rc=$?\"; } > \"$OUT\" 2>&1 || true",
)