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

The old build-apk.sh ran every step in sequence, every time, defending
against itself with a hand-written stamp file for the one step slow enough to
notice. As a graph each step is a target: editing a screen recompiles the
boot image and repackages, editing the Java touches neither the Rust nor the
Scheme, and an unchanged tree does nothing at all.

Nothing here is a compiler toolchain — the native halves come from
jolt-native, so genrule and a python bootstrap are all the graph needs. What
it does need is machine paths, and those are written to .buckconfig.local by
the `buck` recipe rather than looked for from inside a BUCK file. `$(...)` in
a genrule cmd is a buck macro rather than the shell's substitution, which is
why every command substitution below is a backtick.

The library, the glue and glimmer-vidya come from jolt-native's v0.1.0
release, pinned by digest, so an APK needs neither that checkout nor an NDK
anywhere on the machine. A sibling checkout still wins over the pin, because
anyone working on both repos at once should be building what they are
editing. Each is fetched as an archive rather than resolved at run time: the
bytes are then inputs, and a stale library cannot survive a release bump —
which it did, silently, when this shelled out to the other project instead.

build-jolt-boot.sh had gone stale in a way nothing would have reported: it
still read glimmer out of a github cache path and glimmer-vidya out of the
vidya checkout, neither of which is where deps.edn has said they are for some
time. It now takes the shas from deps.edn, and jolt through DotSlash, so the
machine compiling the boot image needs no jolt installed either.
nandi committed 2026-08-31T07:16:15-07:00 Browse files
a71ef8f parent: 5c40e75
added .buckconfig +32 -0
new file mode 100644
@@ -0,0 +1,32 @@
1+[cells]
2+ root = .
3+ prelude = prelude
4+ toolchains = toolchains
5+ none = none
6+
7+[cell_aliases]
8+ config = prelude
9+ ovr_config = prelude
10+ fbcode = none
11+ fbsource = none
12+ fbcode_macros = none
13+ buck = none
14+
15+# The prelude bundled with the buck2 binary, so the only pinned thing is
16+# scripts/buck2 itself.
17+[external_cells]
18+ prelude = bundled
19+
20+# Both of these name the cache-enabled platform, and both are needed: the
21+# second registers it, the first is what actually routes toolchain-owned
22+# actions onto it.
23+[parser]
24+ target_platform_detector_spec = target:root//...->root//platforms:cache \
25+ target:prelude//...->root//platforms:cache \
26+ target:toolchains//...->root//platforms:cache
27+
28+[build]
29+ execution_platforms = root//platforms:cache
30+
31+[project]
32+ ignore = .git,android/build,.jolt-native,buck-out
new file mode 100644
@@ -0,0 +1,32 @@
1+[cells]
2+ root = .
3+ prelude = prelude
4+ toolchains = toolchains
5+ none = none
6+
7+[cell_aliases]
8+ config = prelude
9+ ovr_config = prelude
10+ fbcode = none
11+ fbsource = none
12+ fbcode_macros = none
13+ buck = none
14+
15+# The prelude bundled with the buck2 binary, so the only pinned thing is
16+# scripts/buck2 itself.
17+[external_cells]
18+ prelude = bundled
19+
20+# Both of these name the cache-enabled platform, and both are needed: the
21+# second registers it, the first is what actually routes toolchain-owned
22+# actions onto it.
23+[parser]
24+ target_platform_detector_spec = target:root//...->root//platforms:cache \
25+ target:prelude//...->root//platforms:cache \
26+ target:toolchains//...->root//platforms:cache
27+
28+[build]
29+ execution_platforms = root//platforms:cache
30+
31+[project]
32+ ignore = .git,android/build,.jolt-native,buck-out
modified .gitignore +8 -0
@@ -1,6 +1,14 @@
11 # Build outputs: the boot image, the staged libraries, the APK.
22 /android/build/
33
4+# libvidya, staged out of jolt-native by the `buck` recipe.
5+/android/prebuilt/
6+
7+# buck2 output tree.
8+/buck-out
9+# Written by the `buck` recipe: this machine's paths, not a shared setting.
10+/.buckconfig.local
11+
412 # jolt-native, cloned by `just lib` when there is no sibling checkout.
513 /.jolt-native/
614
@@ -1,6 +1,14 @@
1 # Build outputs: the boot image, the staged libraries, the APK.1 # Build outputs: the boot image, the staged libraries, the APK.
2 /android/build/2 /android/build/
3 3
4+# libvidya, staged out of jolt-native by the `buck` recipe.
5+/android/prebuilt/
6+
7+# buck2 output tree.
8+/buck-out
9+# Written by the `buck` recipe: this machine's paths, not a shared setting.
10+/.buckconfig.local
11+
4 # jolt-native, cloned by `just lib` when there is no sibling checkout.12 # jolt-native, cloned by `just lib` when there is no sibling checkout.
5 /.jolt-native/13 /.jolt-native/
6 14
added BUCK +15 -0
new file mode 100644
@@ -0,0 +1,15 @@
1+# The Jolt sources the boot image is compiled from. Named as a target so that
2+# editing a screen invalidates the image: the compile itself reads the tree
3+# through deps.edn paths rather than through these, but buck only reruns it
4+# when something here changes.
5+filegroup(
6+ name = "jolt-sources",
7+ srcs = glob(["src/**/*.jolt", "src/**/*.edn"]) + ["deps.edn"],
8+ visibility = ["PUBLIC"],
9+)
10+
11+alias(
12+ name = "apk",
13+ actual = "//android:apk",
14+ visibility = ["PUBLIC"],
15+)
new file mode 100644
@@ -0,0 +1,15 @@
1+# The Jolt sources the boot image is compiled from. Named as a target so that
2+# editing a screen invalidates the image: the compile itself reads the tree
3+# through deps.edn paths rather than through these, but buck only reruns it
4+# when something here changes.
5+filegroup(
6+ name = "jolt-sources",
7+ srcs = glob(["src/**/*.jolt", "src/**/*.edn"]) + ["deps.edn"],
8+ visibility = ["PUBLIC"],
9+)
10+
11+alias(
12+ name = "apk",
13+ actual = "//android:apk",
14+ visibility = ["PUBLIC"],
15+)
added android/BUCK +210 -0
new file mode 100644
@@ -0,0 +1,210 @@
1+# The APK, as a graph rather than a script.
2+#
3+# Every step the old build-apk.sh ran in sequence is a target here, so a change
4+# to one screen rebuilds the boot image and repackages, and touches neither the
5+# Rust nor the Java. The two native halves come from jolt-native — libvidya out
6+# of its buck2 graph, the glue compiled by the NDK its scripts/ pin — and the
7+# rest is this repo's.
8+#
9+# The machine-specific paths are read from .buckconfig.local, which the `buck`
10+# recipe in the justfile writes. Nothing here is found by looking around the
11+# machine; if a path is missing the recipe says which.
12+load(":defs.bzl", "libvidya")
13+
14+_JOLT_NATIVE = read_root_config("frq", "jolt_native", "")
15+_ANDROID_HOME = read_root_config("frq", "android_home", "")
16+_CHEZ = read_root_config("frq", "chez_android", "")
17+_OPENSSL = read_root_config("frq", "openssl_android", "")
18+
19+_API = "28"
20+_TOOLS = _ANDROID_HOME + "/build-tools/36.0.0"
21+_ANDROID_JAR = _ANDROID_HOME + "/platforms/android-36/android.jar"
22+# `$(...)` in a genrule cmd is a buck macro, not the shell's substitution, so
23+# everything this file runs at command time uses backticks.
24+# This repo's own, not the other checkout's: an APK build needs a clang and an
25+# llvm-objcopy whether or not jolt-native is on the machine. Both resolve the
26+# NDK through scripts/android-ndk.dotslash, downloading it once.
27+_NDK_BIN = "ndk=`$(location //scripts:android-ndk-bin)`"
28+_CC = "$(location //scripts:android-cc)"
29+
30+# An absolute path for $OUT, so a command may cd without losing it. buck has
31+# already made the parent directory. Spelled out step by step because backticks
32+# do not nest.
33+# Empty when a checkout answers for it, so the script's own lookup wins.
34+# The archive's top directory is what strip_prefix took off, so `src` is
35+# directly inside it. Absolute, because the script is handed this and does its
36+# own directory changing.
37+_GLIMMER_VIDYA = "" if read_root_config("frq", "libvidya", "pinned") == "checkout" else \
38+ "GLIMMER_VIDYA=`realpath $(location toolchains//dist:glimmer-vidya)/src` "
39+
40+# jolt_main.c and the ABI's headers. A sibling checkout wins here too, so that
41+# editing the glue and the library together works; otherwise the release.
42+_GLUE = _JOLT_NATIVE if read_root_config("frq", "libvidya", "pinned") == "checkout" else None
43+_GLUE_C = (_GLUE + "/android/jolt_main.c") if _GLUE else "`realpath $(location toolchains//dist:android-glue)/android/jolt_main.c`"
44+_GLUE_INCLUDE = (_GLUE + "/crates/jolt-vidya/include") if _GLUE else "`realpath $(location toolchains//dist:android-glue)/include`"
45+
46+_ABS_OUT = 'd=`dirname "$OUT"` && b=`basename "$OUT"` && d=`cd "$d" && pwd` && out="$d/$b"'
47+
48+# Somewhere to assemble in. Removed by the command that made it, on success;
49+# buck's own scratch space is not this.
50+_TMP = 'tmp=`mktemp -d`'
51+
52+export_file(
53+ name = "build-jolt-boot.sh",
54+ mode = "reference",
55+)
56+
57+# --- the UI half ------------------------------------------------------------
58+# Two ways in, and both make the library's bytes an input rather than a
59+# command buck would cache forever:
60+#
61+# pinned the .so out of jolt-native's release, fetched by digest. What a
62+# build gets by default, and what makes an APK buildable with no
63+# jolt-native checkout and no NDK anywhere on the machine.
64+# checkout the .so a sibling jolt-native just built, staged into this cell
65+# by the `buck` recipe. Anyone working on both repos at once
66+# builds what they are editing.
67+#
68+# The recipe decides which, by whether that checkout exists, and says so here.
69+libvidya(name = "libvidya")
70+
71+# --- the Jolt half ----------------------------------------------------------
72+# The boot image: frq's Scheme, cross-compiled to arm64 by Chez. The sources
73+# are an input so that editing one rebuilds this; the compile itself reads them
74+# through the deps.edn the script writes.
75+# The sources here are frq's own. The other two roots the image is compiled
76+# from — glimmer, out of the jolt cache, and glimmer-vidya, out of jolt-native
77+# — are outside this cell, and so are Chez's cross boot files and the pinned
78+# jolt itself. None of them can be an input, so the `buck` recipe hashes them
79+# and writes the digest below; naming it in the command is what puts them in
80+# this action's identity.
81+# jolt reaches this action as a manifest and a fetcher rather than as a
82+# program: both are inputs, so nothing here depends on what is installed where
83+# the compile runs, and a remote worker resolves the same pin — by the same
84+# digest — from the same place this machine would.
85+genrule(
86+ name = "jolt-boot",
87+ out = "boot",
88+ srcs = ["//:jolt-sources"],
89+ cmd = "# sources outside this cell: " + read_root_config("frq", "boot_stamp", "unknown") + "\n" +
90+ "DOTSLASH=$(location toolchains//dist:dotslash)/dotslash " +
91+ "JOLT_MANIFEST=$(location //scripts:jolt) " +
92+ # The one source root the script cannot find for itself when there
93+ # is no jolt-native checkout. An input, so a new release recompiles
94+ # the image.
95+ _GLIMMER_VIDYA +
96+ "$(location :build-jolt-boot.sh) \"$OUT\" >&2",
97+)
98+
99+# The image travels as a blob in an object file's data section; the
100+# _binary_jolt_boot_{start,end} symbols jolt_main.c reads are named after the
101+# input *path*, which is why this copies the file somewhere it can be called
102+# exactly `jolt.boot` before converting it.
103+genrule(
104+ name = "jolt-boot-obj",
105+ out = "jolt_boot.o",
106+ cmd = _ABS_OUT + " && " +
107+ _TMP + " && " + _NDK_BIN + " && " +
108+ "cp $(location :jolt-boot)/jolt.boot \"$tmp/jolt.boot\" && " +
109+ "( cd \"$tmp\" && \"$ndk\"/llvm-objcopy " +
110+ "--input-target=binary --output-target=elf64-littleaarch64 " +
111+ "--binary-architecture=aarch64 jolt.boot jolt_boot.o ) && " +
112+ "cp \"$tmp/jolt_boot.o\" \"$out\" && rm -rf \"$tmp\"",
113+)
114+
115+# The glue: jolt-native's android/jolt_main.c over the boot image, linked
116+# against libvidya by name. --no-undefined is what makes a symbol the Scheme
117+# side registers but the ABI no longer exports a build failure here rather than
118+# a crash on the phone.
119+genrule(
120+ name = "libjoltapp",
121+ out = "libjoltapp.so",
122+ cmd = _ABS_OUT + " && lib=`mktemp -d` && " +
123+ "cp $(location :libvidya) \"$lib/libvidya.so\" && " +
124+ _CC + " -shared -fPIC -O2 -o \"$out\" " +
125+ _GLUE_C + " " +
126+ "$(location :jolt-boot-obj) " +
127+ "-I$(location :jolt-boot) " +
128+ "-I" + _GLUE_INCLUDE + " " +
129+ "-L\"$lib\" " +
130+ _CHEZ + "/tarm64le/boot/tarm64le/libkernel.a " +
131+ _CHEZ + "/lz4/lib/liblz4.a " +
132+ "-lvidya -landroid -llog -lz -ldl -lm -Wl,--no-undefined && " +
133+ "rm -rf \"$lib\"",
134+)
135+
136+# --- the Java half ----------------------------------------------------------
137+# One class: the photo chooser's result has to land somewhere, and native code
138+# is not somewhere. android.jar on the class path is where every android.* type
139+# comes from; the JDK's own java.* is what is left.
140+genrule(
141+ name = "classes-dex",
142+ out = "classes.dex",
143+ srcs = glob(["java/**/*.java"]),
144+ cmd = _ABS_OUT + " && " + _TMP + " && " +
145+ "javac --release 17 --class-path " + _ANDROID_JAR + " -d \"$tmp/classes\" $SRCS && " +
146+ "classes=`find \"$tmp/classes\" -name '*.class'` && " +
147+ _TOOLS + "/d8 --min-api " + _API + " --output \"$tmp\" $classes && " +
148+ "cp \"$tmp/classes.dex\" \"$out\" && rm -rf \"$tmp\"",
149+)
150+
151+# --- the package ------------------------------------------------------------
152+# The libraries are stored rather than deflated: the loader maps them straight
153+# out of the APK. The dex is read rather than mapped, so it may as well
154+# compress. OpenSSL travels with the app because the platform's own is not ours
155+# to load — an app's linker namespace refuses /system/lib64/libssl.so, and
156+# without one there is no TLS on the phone at all.
157+genrule(
158+ name = "apk-unsigned",
159+ out = "frq-unsigned.apk",
160+ srcs = ["AndroidManifest.xml"],
161+ cmd = _ABS_OUT + " && stage=`mktemp -d` && " +
162+ "mkdir -p \"$stage/lib/arm64-v8a\" && " +
163+ "cp $(location :libvidya) \"$stage/lib/arm64-v8a/libvidya.so\" && " +
164+ "cp $(location :libjoltapp) \"$stage/lib/arm64-v8a/libjoltapp.so\" && " +
165+ "cp " + _OPENSSL + "/libssl.so " + _OPENSSL + "/libcrypto.so \"$stage/lib/arm64-v8a/\" && " +
166+ "cp $(location :classes-dex) \"$stage/classes.dex\" && " +
167+ _TOOLS + "/aapt2 link -o \"$out\" -I " + _ANDROID_JAR + " " +
168+ "--manifest $SRCS --min-sdk-version " + _API + " --target-sdk-version 36 " +
169+ "--version-code 1 --version-name 0.1.0 >&2 && " +
170+ "( cd \"$stage\" && zip -q -0 \"$out\" lib/arm64-v8a/libvidya.so " +
171+ "lib/arm64-v8a/libjoltapp.so lib/arm64-v8a/libssl.so lib/arm64-v8a/libcrypto.so && " +
172+ "zip -q \"$out\" classes.dex ) && rm -rf \"$stage\"",
173+)
174+
175+# Aligned and signed with the debug key, which is generated on first use the
176+# way the SDK's own tools generate it.
177+genrule(
178+ name = "apk",
179+ out = "frq.apk",
180+ cmd = _ABS_OUT + " && " + _TMP + " && " +
181+ "ks=\"$HOME/.android/debug.keystore\" && " +
182+ "if [ ! -f \"$ks\" ]; then mkdir -p \"$HOME/.android\" && " +
183+ "keytool -genkeypair -v -keystore \"$ks\" -storepass android -keypass android " +
184+ "-alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 " +
185+ "-dname 'CN=Android Debug,O=Android,C=US' >&2; fi && " +
186+ _TOOLS + "/zipalign -f -p 4 $(location :apk-unsigned) \"$tmp/aligned.apk\" && " +
187+ _TOOLS + "/apksigner sign --ks \"$ks\" --ks-key-alias androiddebugkey " +
188+ "--ks-pass pass:android --key-pass pass:android --out \"$out\" \"$tmp/aligned.apk\" && " +
189+ _TOOLS + "/apksigner verify \"$out\" >/dev/null && rm -rf \"$tmp\"",
190+ visibility = ["PUBLIC"],
191+)
192+
193+# Does the pin resolve where the action runs, and does what it resolves to run
194+# there? Nothing depends on this; it is here to be asked by hand.
195+#
196+# buck2 build //android:jolt-fetch-check --remote-only --materializations=all \
197+# -c build.execution_platforms=root//platforms:remote -c "parser...->root//platforms:remote"
198+#
199+# It is what found the glibc floor: jolt needs 2.38, and the rbe-ubuntu22-04
200+# image this platform used to name ships 2.35, so the fetch succeeded and the
201+# binary would not start. The answer was the newer image, not a different pin.
202+genrule(
203+ name = "jolt-fetch-check",
204+ out = "report",
205+ cmd = "jolt=`$(location toolchains//dist:dotslash)/dotslash -- fetch $(location //scripts:jolt)`; " +
206+ "{ echo \"fetched: $jolt\"; " +
207+ "cat /etc/os-release 2>/dev/null | head -1 || true; " +
208+ "ldd --version 2>&1 | head -1 || true; " +
209+ "echo '--- run ---'; \"$jolt\" --version; echo \"rc=$?\"; } > \"$OUT\" 2>&1 || true",
210+)
new file mode 100644
@@ -0,0 +1,210 @@
1+# The APK, as a graph rather than a script.
2+#
3+# Every step the old build-apk.sh ran in sequence is a target here, so a change
4+# to one screen rebuilds the boot image and repackages, and touches neither the
5+# Rust nor the Java. The two native halves come from jolt-native — libvidya out
6+# of its buck2 graph, the glue compiled by the NDK its scripts/ pin — and the
7+# rest is this repo's.
8+#
9+# The machine-specific paths are read from .buckconfig.local, which the `buck`
10+# recipe in the justfile writes. Nothing here is found by looking around the
11+# machine; if a path is missing the recipe says which.
12+load(":defs.bzl", "libvidya")
13+
14+_JOLT_NATIVE = read_root_config("frq", "jolt_native", "")
15+_ANDROID_HOME = read_root_config("frq", "android_home", "")
16+_CHEZ = read_root_config("frq", "chez_android", "")
17+_OPENSSL = read_root_config("frq", "openssl_android", "")
18+
19+_API = "28"
20+_TOOLS = _ANDROID_HOME + "/build-tools/36.0.0"
21+_ANDROID_JAR = _ANDROID_HOME + "/platforms/android-36/android.jar"
22+# `$(...)` in a genrule cmd is a buck macro, not the shell's substitution, so
23+# everything this file runs at command time uses backticks.
24+# This repo's own, not the other checkout's: an APK build needs a clang and an
25+# llvm-objcopy whether or not jolt-native is on the machine. Both resolve the
26+# NDK through scripts/android-ndk.dotslash, downloading it once.
27+_NDK_BIN = "ndk=`$(location //scripts:android-ndk-bin)`"
28+_CC = "$(location //scripts:android-cc)"
29+
30+# An absolute path for $OUT, so a command may cd without losing it. buck has
31+# already made the parent directory. Spelled out step by step because backticks
32+# do not nest.
33+# Empty when a checkout answers for it, so the script's own lookup wins.
34+# The archive's top directory is what strip_prefix took off, so `src` is
35+# directly inside it. Absolute, because the script is handed this and does its
36+# own directory changing.
37+_GLIMMER_VIDYA = "" if read_root_config("frq", "libvidya", "pinned") == "checkout" else \
38+ "GLIMMER_VIDYA=`realpath $(location toolchains//dist:glimmer-vidya)/src` "
39+
40+# jolt_main.c and the ABI's headers. A sibling checkout wins here too, so that
41+# editing the glue and the library together works; otherwise the release.
42+_GLUE = _JOLT_NATIVE if read_root_config("frq", "libvidya", "pinned") == "checkout" else None
43+_GLUE_C = (_GLUE + "/android/jolt_main.c") if _GLUE else "`realpath $(location toolchains//dist:android-glue)/android/jolt_main.c`"
44+_GLUE_INCLUDE = (_GLUE + "/crates/jolt-vidya/include") if _GLUE else "`realpath $(location toolchains//dist:android-glue)/include`"
45+
46+_ABS_OUT = 'd=`dirname "$OUT"` && b=`basename "$OUT"` && d=`cd "$d" && pwd` && out="$d/$b"'
47+
48+# Somewhere to assemble in. Removed by the command that made it, on success;
49+# buck's own scratch space is not this.
50+_TMP = 'tmp=`mktemp -d`'
51+
52+export_file(
53+ name = "build-jolt-boot.sh",
54+ mode = "reference",
55+)
56+
57+# --- the UI half ------------------------------------------------------------
58+# Two ways in, and both make the library's bytes an input rather than a
59+# command buck would cache forever:
60+#
61+# pinned the .so out of jolt-native's release, fetched by digest. What a
62+# build gets by default, and what makes an APK buildable with no
63+# jolt-native checkout and no NDK anywhere on the machine.
64+# checkout the .so a sibling jolt-native just built, staged into this cell
65+# by the `buck` recipe. Anyone working on both repos at once
66+# builds what they are editing.
67+#
68+# The recipe decides which, by whether that checkout exists, and says so here.
69+libvidya(name = "libvidya")
70+
71+# --- the Jolt half ----------------------------------------------------------
72+# The boot image: frq's Scheme, cross-compiled to arm64 by Chez. The sources
73+# are an input so that editing one rebuilds this; the compile itself reads them
74+# through the deps.edn the script writes.
75+# The sources here are frq's own. The other two roots the image is compiled
76+# from — glimmer, out of the jolt cache, and glimmer-vidya, out of jolt-native
77+# — are outside this cell, and so are Chez's cross boot files and the pinned
78+# jolt itself. None of them can be an input, so the `buck` recipe hashes them
79+# and writes the digest below; naming it in the command is what puts them in
80+# this action's identity.
81+# jolt reaches this action as a manifest and a fetcher rather than as a
82+# program: both are inputs, so nothing here depends on what is installed where
83+# the compile runs, and a remote worker resolves the same pin — by the same
84+# digest — from the same place this machine would.
85+genrule(
86+ name = "jolt-boot",
87+ out = "boot",
88+ srcs = ["//:jolt-sources"],
89+ cmd = "# sources outside this cell: " + read_root_config("frq", "boot_stamp", "unknown") + "\n" +
90+ "DOTSLASH=$(location toolchains//dist:dotslash)/dotslash " +
91+ "JOLT_MANIFEST=$(location //scripts:jolt) " +
92+ # The one source root the script cannot find for itself when there
93+ # is no jolt-native checkout. An input, so a new release recompiles
94+ # the image.
95+ _GLIMMER_VIDYA +
96+ "$(location :build-jolt-boot.sh) \"$OUT\" >&2",
97+)
98+
99+# The image travels as a blob in an object file's data section; the
100+# _binary_jolt_boot_{start,end} symbols jolt_main.c reads are named after the
101+# input *path*, which is why this copies the file somewhere it can be called
102+# exactly `jolt.boot` before converting it.
103+genrule(
104+ name = "jolt-boot-obj",
105+ out = "jolt_boot.o",
106+ cmd = _ABS_OUT + " && " +
107+ _TMP + " && " + _NDK_BIN + " && " +
108+ "cp $(location :jolt-boot)/jolt.boot \"$tmp/jolt.boot\" && " +
109+ "( cd \"$tmp\" && \"$ndk\"/llvm-objcopy " +
110+ "--input-target=binary --output-target=elf64-littleaarch64 " +
111+ "--binary-architecture=aarch64 jolt.boot jolt_boot.o ) && " +
112+ "cp \"$tmp/jolt_boot.o\" \"$out\" && rm -rf \"$tmp\"",
113+)
114+
115+# The glue: jolt-native's android/jolt_main.c over the boot image, linked
116+# against libvidya by name. --no-undefined is what makes a symbol the Scheme
117+# side registers but the ABI no longer exports a build failure here rather than
118+# a crash on the phone.
119+genrule(
120+ name = "libjoltapp",
121+ out = "libjoltapp.so",
122+ cmd = _ABS_OUT + " && lib=`mktemp -d` && " +
123+ "cp $(location :libvidya) \"$lib/libvidya.so\" && " +
124+ _CC + " -shared -fPIC -O2 -o \"$out\" " +
125+ _GLUE_C + " " +
126+ "$(location :jolt-boot-obj) " +
127+ "-I$(location :jolt-boot) " +
128+ "-I" + _GLUE_INCLUDE + " " +
129+ "-L\"$lib\" " +
130+ _CHEZ + "/tarm64le/boot/tarm64le/libkernel.a " +
131+ _CHEZ + "/lz4/lib/liblz4.a " +
132+ "-lvidya -landroid -llog -lz -ldl -lm -Wl,--no-undefined && " +
133+ "rm -rf \"$lib\"",
134+)
135+
136+# --- the Java half ----------------------------------------------------------
137+# One class: the photo chooser's result has to land somewhere, and native code
138+# is not somewhere. android.jar on the class path is where every android.* type
139+# comes from; the JDK's own java.* is what is left.
140+genrule(
141+ name = "classes-dex",
142+ out = "classes.dex",
143+ srcs = glob(["java/**/*.java"]),
144+ cmd = _ABS_OUT + " && " + _TMP + " && " +
145+ "javac --release 17 --class-path " + _ANDROID_JAR + " -d \"$tmp/classes\" $SRCS && " +
146+ "classes=`find \"$tmp/classes\" -name '*.class'` && " +
147+ _TOOLS + "/d8 --min-api " + _API + " --output \"$tmp\" $classes && " +
148+ "cp \"$tmp/classes.dex\" \"$out\" && rm -rf \"$tmp\"",
149+)
150+
151+# --- the package ------------------------------------------------------------
152+# The libraries are stored rather than deflated: the loader maps them straight
153+# out of the APK. The dex is read rather than mapped, so it may as well
154+# compress. OpenSSL travels with the app because the platform's own is not ours
155+# to load — an app's linker namespace refuses /system/lib64/libssl.so, and
156+# without one there is no TLS on the phone at all.
157+genrule(
158+ name = "apk-unsigned",
159+ out = "frq-unsigned.apk",
160+ srcs = ["AndroidManifest.xml"],
161+ cmd = _ABS_OUT + " && stage=`mktemp -d` && " +
162+ "mkdir -p \"$stage/lib/arm64-v8a\" && " +
163+ "cp $(location :libvidya) \"$stage/lib/arm64-v8a/libvidya.so\" && " +
164+ "cp $(location :libjoltapp) \"$stage/lib/arm64-v8a/libjoltapp.so\" && " +
165+ "cp " + _OPENSSL + "/libssl.so " + _OPENSSL + "/libcrypto.so \"$stage/lib/arm64-v8a/\" && " +
166+ "cp $(location :classes-dex) \"$stage/classes.dex\" && " +
167+ _TOOLS + "/aapt2 link -o \"$out\" -I " + _ANDROID_JAR + " " +
168+ "--manifest $SRCS --min-sdk-version " + _API + " --target-sdk-version 36 " +
169+ "--version-code 1 --version-name 0.1.0 >&2 && " +
170+ "( cd \"$stage\" && zip -q -0 \"$out\" lib/arm64-v8a/libvidya.so " +
171+ "lib/arm64-v8a/libjoltapp.so lib/arm64-v8a/libssl.so lib/arm64-v8a/libcrypto.so && " +
172+ "zip -q \"$out\" classes.dex ) && rm -rf \"$stage\"",
173+)
174+
175+# Aligned and signed with the debug key, which is generated on first use the
176+# way the SDK's own tools generate it.
177+genrule(
178+ name = "apk",
179+ out = "frq.apk",
180+ cmd = _ABS_OUT + " && " + _TMP + " && " +
181+ "ks=\"$HOME/.android/debug.keystore\" && " +
182+ "if [ ! -f \"$ks\" ]; then mkdir -p \"$HOME/.android\" && " +
183+ "keytool -genkeypair -v -keystore \"$ks\" -storepass android -keypass android " +
184+ "-alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 " +
185+ "-dname 'CN=Android Debug,O=Android,C=US' >&2; fi && " +
186+ _TOOLS + "/zipalign -f -p 4 $(location :apk-unsigned) \"$tmp/aligned.apk\" && " +
187+ _TOOLS + "/apksigner sign --ks \"$ks\" --ks-key-alias androiddebugkey " +
188+ "--ks-pass pass:android --key-pass pass:android --out \"$out\" \"$tmp/aligned.apk\" && " +
189+ _TOOLS + "/apksigner verify \"$out\" >/dev/null && rm -rf \"$tmp\"",
190+ visibility = ["PUBLIC"],
191+)
192+
193+# Does the pin resolve where the action runs, and does what it resolves to run
194+# there? Nothing depends on this; it is here to be asked by hand.
195+#
196+# buck2 build //android:jolt-fetch-check --remote-only --materializations=all \
197+# -c build.execution_platforms=root//platforms:remote -c "parser...->root//platforms:remote"
198+#
199+# It is what found the glibc floor: jolt needs 2.38, and the rbe-ubuntu22-04
200+# image this platform used to name ships 2.35, so the fetch succeeded and the
201+# binary would not start. The answer was the newer image, not a different pin.
202+genrule(
203+ name = "jolt-fetch-check",
204+ out = "report",
205+ cmd = "jolt=`$(location toolchains//dist:dotslash)/dotslash -- fetch $(location //scripts:jolt)`; " +
206+ "{ echo \"fetched: $jolt\"; " +
207+ "cat /etc/os-release 2>/dev/null | head -1 || true; " +
208+ "ldd --version 2>&1 | head -1 || true; " +
209+ "echo '--- run ---'; \"$jolt\" --version; echo \"rc=$?\"; } > \"$OUT\" 2>&1 || true",
210+)
modified android/build-apk.sh +29 -10
@@ -4,8 +4,8 @@
44 # libvidya.so the C ABI on Rust/egui, cross-compiled by buck2, and the
55 # NativeActivity's own library (it holds android-activity's
66 # glue, so it owns the event loop)
7-# libjoltapp.so vidya's android/jolt_main.c plus frq's Jolt boot image,
8-# dlopened by the above
7+# libjoltapp.so jolt-native's android/jolt_main.c plus frq's Jolt boot
8+# image, dlopened by the above
99 # classes.dex one Java class, and only because a picture chooser answers
1010 # through onActivityResult and a NativeActivity has nowhere to
1111 # deliver that
@@ -14,12 +14,29 @@
1414 # without one there is no TLS at all on the phone
1515 #
1616 # Neither half is built here beyond that last link: the UI library comes from
17-# vidya's `just ffi-android` and the boot image from build-jolt-boot.sh. Both
18-# native pieces are vidya's — only the boot image is frq's.
17+# jolt-native's `just ffi-android` and the boot image from build-jolt-boot.sh.
18+# Both native pieces are jolt-native's — only the boot image is frq's.
1919 set -euo pipefail
2020
2121 ROOT="$(cd "$(dirname "$0")/.." && pwd)"
22-VIDYA="${VIDYA:-$(cd "$ROOT/../vidya" && pwd)}"
22+
23+# Where jolt-native is, answered the same way the justfile answers it: a
24+# sibling checkout wins, and otherwise it is the clone `just lib` leaves under
25+# .jolt-native. --git-common-dir rather than the working tree because this
26+# script runs from a worktree as readily as from the checkout, and in one of
27+# those "../jolt-native" is not a sibling of anything.
28+CHECKOUT="$(dirname "$(git -C "$ROOT" rev-parse --path-format=absolute --git-common-dir)")"
29+if [[ -z "${JOLT_NATIVE:-}" ]]; then
30+ if [[ -d "$CHECKOUT/../jolt-native" ]]; then
31+ JOLT_NATIVE="$(cd "$CHECKOUT/../jolt-native" && pwd)"
32+ else
33+ JOLT_NATIVE="$CHECKOUT/.jolt-native"
34+ fi
35+fi
36+[[ -d "$JOLT_NATIVE" ]] || {
37+ echo "no jolt-native at $JOLT_NATIVE — run \`just lib\` to clone it" >&2
38+ exit 1
39+}
2340 ANDROID_HOME="${ANDROID_HOME:-$HOME/.local/share/android-sdk}"
2441 ANDROID_NDK_HOME="${ANDROID_NDK_HOME:-$HOME/.local/share/android-ndk-r29}"
2542 CHEZ_ANDROID="${CHEZ_ANDROID:-$HOME/.cache/vidya-chez-android}"
@@ -43,8 +60,11 @@ for path in \
4360 done
4461
4562 # --- the UI half ------------------------------------------------------------
46-( cd "$VIDYA" && just ffi-android >&2 )
47-VIDYA_SO="$VIDYA/build/android/arm64-v8a/libvidya.so"
63+# buck2 fetches its own NDK for this, from the pin in jolt-native's
64+# scripts/android-ndk.dotslash, so the toolchain below is the only one that has
65+# to be installed by hand.
66+( cd "$JOLT_NATIVE" && just ffi-android >&2 )
67+VIDYA_SO="$JOLT_NATIVE/build/android/arm64-v8a/libvidya.so"
4868 [[ -f "$VIDYA_SO" ]] || { echo "missing $VIDYA_SO" >&2; exit 1; }
4969
5070 # --- the Jolt half ----------------------------------------------------------
@@ -89,11 +109,10 @@ cp "$OPENSSL_ANDROID/libssl.so" "$OPENSSL_ANDROID/libcrypto.so" \
89109 "$NDK_BIN/aarch64-linux-android$API-clang" \
90110 -shared -fPIC -O2 \
91111 -o "$STAGE/lib/arm64-v8a/libjoltapp.so" \
92- "$VIDYA/android/jolt_main.c" \
112+ "$JOLT_NATIVE/android/jolt_main.c" \
93113 "$JOLT_BUILD/jolt_boot.o" \
94114 -I"$JOLT_BUILD" \
95- -I"$VIDYA/raylib/include" \
96- -I"$VIDYA/ffi/include" \
115+ -I"$JOLT_NATIVE/crates/jolt-vidya/include" \
97116 -L"$STAGE/lib/arm64-v8a" \
98117 "$CHEZ_ANDROID/tarm64le/boot/tarm64le/libkernel.a" \
99118 "$CHEZ_ANDROID/lz4/lib/liblz4.a" \
@@ -4,8 +4,8 @@
4 # libvidya.so the C ABI on Rust/egui, cross-compiled by buck2, and the4 # libvidya.so the C ABI on Rust/egui, cross-compiled by buck2, and the
5 # NativeActivity's own library (it holds android-activity's5 # NativeActivity's own library (it holds android-activity's
6 # glue, so it owns the event loop)6 # glue, so it owns the event loop)
7-# libjoltapp.so vidya's android/jolt_main.c plus frq's Jolt boot image,7+# libjoltapp.so jolt-native's android/jolt_main.c plus frq's Jolt boot
8-# dlopened by the above8+# image, dlopened by the above
9 # classes.dex one Java class, and only because a picture chooser answers9 # classes.dex one Java class, and only because a picture chooser answers
10 # through onActivityResult and a NativeActivity has nowhere to10 # through onActivityResult and a NativeActivity has nowhere to
11 # deliver that11 # deliver that
@@ -14,12 +14,29 @@
14 # without one there is no TLS at all on the phone14 # without one there is no TLS at all on the phone
15 #15 #
16 # Neither half is built here beyond that last link: the UI library comes from16 # Neither half is built here beyond that last link: the UI library comes from
17-# vidya's `just ffi-android` and the boot image from build-jolt-boot.sh. Both17+# jolt-native's `just ffi-android` and the boot image from build-jolt-boot.sh.
18-# native pieces are vidya's — only the boot image is frq's.18+# Both native pieces are jolt-native's — only the boot image is frq's.
19 set -euo pipefail19 set -euo pipefail
20 20
21 ROOT="$(cd "$(dirname "$0")/.." && pwd)"21 ROOT="$(cd "$(dirname "$0")/.." && pwd)"
22-VIDYA="${VIDYA:-$(cd "$ROOT/../vidya" && pwd)}"22+
23+# Where jolt-native is, answered the same way the justfile answers it: a
24+# sibling checkout wins, and otherwise it is the clone `just lib` leaves under
25+# .jolt-native. --git-common-dir rather than the working tree because this
26+# script runs from a worktree as readily as from the checkout, and in one of
27+# those "../jolt-native" is not a sibling of anything.
28+CHECKOUT="$(dirname "$(git -C "$ROOT" rev-parse --path-format=absolute --git-common-dir)")"
29+if [[ -z "${JOLT_NATIVE:-}" ]]; then
30+ if [[ -d "$CHECKOUT/../jolt-native" ]]; then
31+ JOLT_NATIVE="$(cd "$CHECKOUT/../jolt-native" && pwd)"
32+ else
33+ JOLT_NATIVE="$CHECKOUT/.jolt-native"
34+ fi
35+fi
36+[[ -d "$JOLT_NATIVE" ]] || {
37+ echo "no jolt-native at $JOLT_NATIVE — run \`just lib\` to clone it" >&2
38+ exit 1
39+}
23 ANDROID_HOME="${ANDROID_HOME:-$HOME/.local/share/android-sdk}"40 ANDROID_HOME="${ANDROID_HOME:-$HOME/.local/share/android-sdk}"
24 ANDROID_NDK_HOME="${ANDROID_NDK_HOME:-$HOME/.local/share/android-ndk-r29}"41 ANDROID_NDK_HOME="${ANDROID_NDK_HOME:-$HOME/.local/share/android-ndk-r29}"
25 CHEZ_ANDROID="${CHEZ_ANDROID:-$HOME/.cache/vidya-chez-android}"42 CHEZ_ANDROID="${CHEZ_ANDROID:-$HOME/.cache/vidya-chez-android}"
@@ -43,8 +60,11 @@ for path in \
43 done60 done
44 61
45 # --- the UI half ------------------------------------------------------------62 # --- the UI half ------------------------------------------------------------
46-( cd "$VIDYA" && just ffi-android >&2 )63+# buck2 fetches its own NDK for this, from the pin in jolt-native's
47-VIDYA_SO="$VIDYA/build/android/arm64-v8a/libvidya.so"64+# scripts/android-ndk.dotslash, so the toolchain below is the only one that has
65+# to be installed by hand.
66+( cd "$JOLT_NATIVE" && just ffi-android >&2 )
67+VIDYA_SO="$JOLT_NATIVE/build/android/arm64-v8a/libvidya.so"
48 [[ -f "$VIDYA_SO" ]] || { echo "missing $VIDYA_SO" >&2; exit 1; }68 [[ -f "$VIDYA_SO" ]] || { echo "missing $VIDYA_SO" >&2; exit 1; }
49 69
50 # --- the Jolt half ----------------------------------------------------------70 # --- the Jolt half ----------------------------------------------------------
@@ -89,11 +109,10 @@ cp "$OPENSSL_ANDROID/libssl.so" "$OPENSSL_ANDROID/libcrypto.so" \
89 "$NDK_BIN/aarch64-linux-android$API-clang" \109 "$NDK_BIN/aarch64-linux-android$API-clang" \
90 -shared -fPIC -O2 \110 -shared -fPIC -O2 \
91 -o "$STAGE/lib/arm64-v8a/libjoltapp.so" \111 -o "$STAGE/lib/arm64-v8a/libjoltapp.so" \
92- "$VIDYA/android/jolt_main.c" \112+ "$JOLT_NATIVE/android/jolt_main.c" \
93 "$JOLT_BUILD/jolt_boot.o" \113 "$JOLT_BUILD/jolt_boot.o" \
94 -I"$JOLT_BUILD" \114 -I"$JOLT_BUILD" \
95- -I"$VIDYA/raylib/include" \115+ -I"$JOLT_NATIVE/crates/jolt-vidya/include" \
96- -I"$VIDYA/ffi/include" \
97 -L"$STAGE/lib/arm64-v8a" \116 -L"$STAGE/lib/arm64-v8a" \
98 "$CHEZ_ANDROID/tarm64le/boot/tarm64le/libkernel.a" \117 "$CHEZ_ANDROID/tarm64le/boot/tarm64le/libkernel.a" \
99 "$CHEZ_ANDROID/lz4/lib/liblz4.a" \118 "$CHEZ_ANDROID/lz4/lib/liblz4.a" \
modified android/build-jolt-boot.sh +75 -17
@@ -2,19 +2,69 @@
22 set -euo pipefail
33
44 ROOT="$(cd "$(dirname "$0")/.." && pwd)"
5-VIDYA="${VIDYA:-$(cd "$ROOT/../vidya" && pwd)}"
6-# glimmer itself is a git dependency, so its sources live in the jolt cache
7-# rather than in either checkout. The boot image is built from :paths alone —
8-# there is no dependency resolution inside a cross compile — so the cache path
9-# has to be named here.
10-GLIMMER="${GLIMMER:-$HOME/.jolt/gitlibs/https___github.com_jolt-lang_glimmer/5581c331c51aff989259b9e8e92ec920fe5e6741/src}"
11-OUT="${1:?usage: build-jolt-boot.sh OUTPUT_DIRECTORY}"
12-# The DotSlash-pinned jolt beside this script, not whatever is on PATH: an
13-# upstream jolt cannot open a TLS connection on Android — it reads the socket
14-# address out of `struct addrinfo` at glibc's offset, which is Bionic's
15-# `ai_canonname` — so a build made with one produces an APK that cannot sign in
16-# or send a picture. Override with JOLT= to use another.
17-JOLT="${JOLT:-$ROOT/scripts/jolt}"
5+
6+# The boot image is built from :paths alone — there is no dependency
7+# resolution inside a cross compile — so every source root deps.edn would have
8+# resolved has to be named here instead. Two of them are git dependencies,
9+# which means the jolt cache rather than a checkout; the shas come out of
10+# deps.edn so there is one place to bump them.
11+CHECKOUT="$(dirname "$(git -C "$ROOT" rev-parse --path-format=absolute --git-common-dir)")"
12+if [[ -z "${JOLT_NATIVE:-}" ]]; then
13+ if [[ -d "$CHECKOUT/../jolt-native" ]]; then
14+ JOLT_NATIVE="$(cd "$CHECKOUT/../jolt-native" && pwd)"
15+ else
16+ JOLT_NATIVE="$CHECKOUT/.jolt-native"
17+ fi
18+fi
19+
20+# The sha deps.edn pins for a given git url, so a bump there reaches this.
21+dep_sha() {
22+ awk -v url="$1" '
23+ index($0, url) { found = 1 }
24+ found && $1 == ":git/sha" { gsub(/[^0-9a-f]/, "", $2); print $2; exit }
25+ ' "$ROOT/deps.edn"
26+}
27+GLIMMER_SHA="$(dep_sha https://gitlab.com/nandithebull/glimmer)"
28+GLIMMER="${GLIMMER:-$HOME/.jolt/gitlibs/https___gitlab.com_nandithebull_glimmer/$GLIMMER_SHA/src}"
29+
30+# glimmer-vidya lives inside jolt-native, so a sibling checkout answers for it
31+# the way it answers for libvidya; the cache is the fallback, at the sha
32+# deps.edn pins.
33+GLIMMER_VIDYA_SHA="$(dep_sha https://gitlab.com/nandithebull/jolt-native)"
34+if [[ -z "${GLIMMER_VIDYA:-}" ]]; then
35+ if [[ -d "$JOLT_NATIVE/jolt/glimmer-vidya/src" ]]; then
36+ GLIMMER_VIDYA="$JOLT_NATIVE/jolt/glimmer-vidya/src"
37+ else
38+ GLIMMER_VIDYA="$HOME/.jolt/gitlibs/https___gitlab.com_nandithebull_jolt-native/$GLIMMER_VIDYA_SHA/jolt/glimmer-vidya/src"
39+ fi
40+fi
41+
42+for path in "$GLIMMER" "$GLIMMER_VIDYA"; do
43+ [[ -d "$path" ]] || {
44+ echo "missing Jolt source root: $path" >&2
45+ echo "run \`jolt -M:frq --help\` once to populate the git cache" >&2
46+ exit 1
47+ }
48+done
49+OUT="${1:?usage: build-jolt-boot.sh OUTPUT_DIRECTORY | --stamp}"
50+# The DotSlash-pinned jolt, not whatever is on PATH: an upstream jolt cannot
51+# open a TLS connection on Android — it reads the socket address out of
52+# `struct addrinfo` at glibc's offset, which is Bionic's `ai_canonname` — so a
53+# build made with one produces an APK that cannot sign in or send a picture.
54+# Override with JOLT= to use another.
55+#
56+# DOTSLASH and JOLT_MANIFEST are set when buck runs this: the manifest and the
57+# fetcher are inputs to that action, so the machine running it needs neither
58+# jolt nor DotSlash installed, and a remote worker resolves the same pin
59+# against the same digest. Without them the shim beside this script answers,
60+# which is what a person at a terminal gets.
61+if [[ -z "${JOLT:-}" ]]; then
62+ if [[ -n "${DOTSLASH:-}" && -n "${JOLT_MANIFEST:-}" ]]; then
63+ JOLT="$("$DOTSLASH" -- fetch "$JOLT_MANIFEST")"
64+ else
65+ JOLT="$ROOT/scripts/jolt"
66+ fi
67+fi
1868 MODULE="${MODULE:-frq.app}"
1969 CHEZ_ANDROID="${CHEZ_ANDROID:-$HOME/.cache/vidya-chez-android}"
2070 HOST_SCHEME="$CHEZ_ANDROID/ta6le/bin/ta6le/scheme"
@@ -25,11 +75,11 @@ for path in "$HOST_SCHEME" "$TARGET_BOOT/petite.boot" \
2575 "$TARGET_BOOT/scheme.boot" "$TARGET_BOOT/scheme.h" "$XPATCH"; do
2676 [[ -e "$path" ]] || {
2777 echo "missing Android Chez artifact: $path" >&2
28- echo "Build Chez's tarm64le cross target first; see ../vidya/android." >&2
78+ echo "Build Chez's tarm64le cross target first." >&2
2979 exit 1
3080 }
3181 done
32-command -v "$JOLT" >/dev/null || {
82+[[ -x "$JOLT" ]] || command -v "$JOLT" >/dev/null || {
3383 echo "Jolt executable not found: $JOLT" >&2
3484 exit 1
3585 }
@@ -48,12 +98,20 @@ stamp_now() {
4898 {
4999 printf '%s\n' "$MODULE" "JOLT_NO_FLAT_SPLIT=1"
50100 "$JOLT" --version 2>/dev/null || true
51- find "$ROOT/src" "$VIDYA/glimmer/src" "$GLIMMER" -type f \
101+ find "$ROOT/src" "$GLIMMER" "$GLIMMER_VIDYA" -type f \
52102 \( -name '*.jolt' -o -name '*.edn' \) -print0 | sort -z | xargs -0 sha256sum
53103 sha256sum "$TARGET_BOOT/petite.boot" "$TARGET_BOOT/scheme.boot" "$XPATCH"
54104 } | sha256sum | cut -d' ' -f1
55105 }
56106
107+# buck needs this before the work rather than after: the sources it hashes live
108+# in the jolt cache and in jolt-native, outside this cell, so nothing else makes
109+# them reach an action's digest. See the `buck` recipe in the justfile.
110+if [[ "${1:-}" == "--stamp" ]]; then
111+ stamp_now
112+ exit 0
113+fi
114+
57115 WANT="$(stamp_now)"
58116 if [[ -f "$OUT/jolt.boot" && -f "$OUT/scheme.h" && -f "$STAMP" ]] &&
59117 [[ "$(<"$STAMP")" == "$WANT" ]]; then
@@ -65,7 +123,7 @@ rm -f "$STAMP"
65123 rm -rf "$OUT/project" "$OUT/cross"
66124 mkdir -p "$OUT/project" "$OUT/cross"
67125 cat > "$OUT/project/deps.edn" <<EOF
68-{:paths ["$ROOT/src" "$VIDYA/glimmer/src" "$GLIMMER"]}
126+{:paths ["$ROOT/src" "$GLIMMER" "$GLIMMER_VIDYA"]}
69127 EOF
70128
71129 (
@@ -2,19 +2,69 @@
2 set -euo pipefail2 set -euo pipefail
3 3
4 ROOT="$(cd "$(dirname "$0")/.." && pwd)"4 ROOT="$(cd "$(dirname "$0")/.." && pwd)"
5-VIDYA="${VIDYA:-$(cd "$ROOT/../vidya" && pwd)}"5+
6-# glimmer itself is a git dependency, so its sources live in the jolt cache6+# The boot image is built from :paths alone — there is no dependency
7-# rather than in either checkout. The boot image is built from :paths alone —7+# resolution inside a cross compile — so every source root deps.edn would have
8-# there is no dependency resolution inside a cross compile — so the cache path8+# resolved has to be named here instead. Two of them are git dependencies,
9-# has to be named here.9+# which means the jolt cache rather than a checkout; the shas come out of
10-GLIMMER="${GLIMMER:-$HOME/.jolt/gitlibs/https___github.com_jolt-lang_glimmer/5581c331c51aff989259b9e8e92ec920fe5e6741/src}"10+# deps.edn so there is one place to bump them.
11-OUT="${1:?usage: build-jolt-boot.sh OUTPUT_DIRECTORY}"11+CHECKOUT="$(dirname "$(git -C "$ROOT" rev-parse --path-format=absolute --git-common-dir)")"
12-# The DotSlash-pinned jolt beside this script, not whatever is on PATH: an12+if [[ -z "${JOLT_NATIVE:-}" ]]; then
13-# upstream jolt cannot open a TLS connection on Android — it reads the socket13+ if [[ -d "$CHECKOUT/../jolt-native" ]]; then
14-# address out of `struct addrinfo` at glibc's offset, which is Bionic's14+ JOLT_NATIVE="$(cd "$CHECKOUT/../jolt-native" && pwd)"
15-# `ai_canonname` — so a build made with one produces an APK that cannot sign in15+ else
16-# or send a picture. Override with JOLT= to use another.16+ JOLT_NATIVE="$CHECKOUT/.jolt-native"
17-JOLT="${JOLT:-$ROOT/scripts/jolt}"17+ fi
18+fi
19+
20+# The sha deps.edn pins for a given git url, so a bump there reaches this.
21+dep_sha() {
22+ awk -v url="$1" '
23+ index($0, url) { found = 1 }
24+ found && $1 == ":git/sha" { gsub(/[^0-9a-f]/, "", $2); print $2; exit }
25+ ' "$ROOT/deps.edn"
26+}
27+GLIMMER_SHA="$(dep_sha https://gitlab.com/nandithebull/glimmer)"
28+GLIMMER="${GLIMMER:-$HOME/.jolt/gitlibs/https___gitlab.com_nandithebull_glimmer/$GLIMMER_SHA/src}"
29+
30+# glimmer-vidya lives inside jolt-native, so a sibling checkout answers for it
31+# the way it answers for libvidya; the cache is the fallback, at the sha
32+# deps.edn pins.
33+GLIMMER_VIDYA_SHA="$(dep_sha https://gitlab.com/nandithebull/jolt-native)"
34+if [[ -z "${GLIMMER_VIDYA:-}" ]]; then
35+ if [[ -d "$JOLT_NATIVE/jolt/glimmer-vidya/src" ]]; then
36+ GLIMMER_VIDYA="$JOLT_NATIVE/jolt/glimmer-vidya/src"
37+ else
38+ GLIMMER_VIDYA="$HOME/.jolt/gitlibs/https___gitlab.com_nandithebull_jolt-native/$GLIMMER_VIDYA_SHA/jolt/glimmer-vidya/src"
39+ fi
40+fi
41+
42+for path in "$GLIMMER" "$GLIMMER_VIDYA"; do
43+ [[ -d "$path" ]] || {
44+ echo "missing Jolt source root: $path" >&2
45+ echo "run \`jolt -M:frq --help\` once to populate the git cache" >&2
46+ exit 1
47+ }
48+done
49+OUT="${1:?usage: build-jolt-boot.sh OUTPUT_DIRECTORY | --stamp}"
50+# The DotSlash-pinned jolt, not whatever is on PATH: an upstream jolt cannot
51+# open a TLS connection on Android — it reads the socket address out of
52+# `struct addrinfo` at glibc's offset, which is Bionic's `ai_canonname` — so a
53+# build made with one produces an APK that cannot sign in or send a picture.
54+# Override with JOLT= to use another.
55+#
56+# DOTSLASH and JOLT_MANIFEST are set when buck runs this: the manifest and the
57+# fetcher are inputs to that action, so the machine running it needs neither
58+# jolt nor DotSlash installed, and a remote worker resolves the same pin
59+# against the same digest. Without them the shim beside this script answers,
60+# which is what a person at a terminal gets.
61+if [[ -z "${JOLT:-}" ]]; then
62+ if [[ -n "${DOTSLASH:-}" && -n "${JOLT_MANIFEST:-}" ]]; then
63+ JOLT="$("$DOTSLASH" -- fetch "$JOLT_MANIFEST")"
64+ else
65+ JOLT="$ROOT/scripts/jolt"
66+ fi
67+fi
18 MODULE="${MODULE:-frq.app}"68 MODULE="${MODULE:-frq.app}"
19 CHEZ_ANDROID="${CHEZ_ANDROID:-$HOME/.cache/vidya-chez-android}"69 CHEZ_ANDROID="${CHEZ_ANDROID:-$HOME/.cache/vidya-chez-android}"
20 HOST_SCHEME="$CHEZ_ANDROID/ta6le/bin/ta6le/scheme"70 HOST_SCHEME="$CHEZ_ANDROID/ta6le/bin/ta6le/scheme"
@@ -25,11 +75,11 @@ for path in "$HOST_SCHEME" "$TARGET_BOOT/petite.boot" \
25 "$TARGET_BOOT/scheme.boot" "$TARGET_BOOT/scheme.h" "$XPATCH"; do75 "$TARGET_BOOT/scheme.boot" "$TARGET_BOOT/scheme.h" "$XPATCH"; do
26 [[ -e "$path" ]] || {76 [[ -e "$path" ]] || {
27 echo "missing Android Chez artifact: $path" >&277 echo "missing Android Chez artifact: $path" >&2
28- echo "Build Chez's tarm64le cross target first; see ../vidya/android." >&278+ echo "Build Chez's tarm64le cross target first." >&2
29 exit 179 exit 1
30 }80 }
31 done81 done
32-command -v "$JOLT" >/dev/null || {82+[[ -x "$JOLT" ]] || command -v "$JOLT" >/dev/null || {
33 echo "Jolt executable not found: $JOLT" >&283 echo "Jolt executable not found: $JOLT" >&2
34 exit 184 exit 1
35 }85 }
@@ -48,12 +98,20 @@ stamp_now() {
48 {98 {
49 printf '%s\n' "$MODULE" "JOLT_NO_FLAT_SPLIT=1"99 printf '%s\n' "$MODULE" "JOLT_NO_FLAT_SPLIT=1"
50 "$JOLT" --version 2>/dev/null || true100 "$JOLT" --version 2>/dev/null || true
51- find "$ROOT/src" "$VIDYA/glimmer/src" "$GLIMMER" -type f \101+ find "$ROOT/src" "$GLIMMER" "$GLIMMER_VIDYA" -type f \
52 \( -name '*.jolt' -o -name '*.edn' \) -print0 | sort -z | xargs -0 sha256sum102 \( -name '*.jolt' -o -name '*.edn' \) -print0 | sort -z | xargs -0 sha256sum
53 sha256sum "$TARGET_BOOT/petite.boot" "$TARGET_BOOT/scheme.boot" "$XPATCH"103 sha256sum "$TARGET_BOOT/petite.boot" "$TARGET_BOOT/scheme.boot" "$XPATCH"
54 } | sha256sum | cut -d' ' -f1104 } | sha256sum | cut -d' ' -f1
55 }105 }
56 106
107+# buck needs this before the work rather than after: the sources it hashes live
108+# in the jolt cache and in jolt-native, outside this cell, so nothing else makes
109+# them reach an action's digest. See the `buck` recipe in the justfile.
110+if [[ "${1:-}" == "--stamp" ]]; then
111+ stamp_now
112+ exit 0
113+fi
114+
57 WANT="$(stamp_now)"115 WANT="$(stamp_now)"
58 if [[ -f "$OUT/jolt.boot" && -f "$OUT/scheme.h" && -f "$STAMP" ]] &&116 if [[ -f "$OUT/jolt.boot" && -f "$OUT/scheme.h" && -f "$STAMP" ]] &&
59 [[ "$(<"$STAMP")" == "$WANT" ]]; then117 [[ "$(<"$STAMP")" == "$WANT" ]]; then
@@ -65,7 +123,7 @@ rm -f "$STAMP"
65 rm -rf "$OUT/project" "$OUT/cross"123 rm -rf "$OUT/project" "$OUT/cross"
66 mkdir -p "$OUT/project" "$OUT/cross"124 mkdir -p "$OUT/project" "$OUT/cross"
67 cat > "$OUT/project/deps.edn" <<EOF125 cat > "$OUT/project/deps.edn" <<EOF
68-{:paths ["$ROOT/src" "$VIDYA/glimmer/src" "$GLIMMER"]}126+{:paths ["$ROOT/src" "$GLIMMER" "$GLIMMER_VIDYA"]}
69 EOF127 EOF
70 128
71 (129 (
added android/defs.bzl +28 -0
new file mode 100644
@@ -0,0 +1,28 @@
1+# Where libvidya comes from, which is a question with two answers.
2+#
3+# A BUCK file cannot branch — `if` outside a `def` is not this dialect — and
4+# this is not a `select` either: it does not vary by configuration but by
5+# whether a checkout is sitting next to this one. So it lives in a macro.
6+def libvidya(name):
7+ """The Android libvidya: a sibling checkout's, or the pinned release's.
8+
9+ Either way the library's bytes are an input to what reads them, which is
10+ the part that matters — an action that shelled out to build it would have
11+ nothing to invalidate on and would serve the same stale object forever.
12+ """
13+ if native.read_root_config("frq", "libvidya", "pinned") == "checkout":
14+ # Staged by the `buck` recipe out of the sibling jolt-native, so that
15+ # anyone working on both repos at once builds what they are editing.
16+ native.export_file(
17+ name = name,
18+ src = "prebuilt/arm64-v8a/libvidya.so",
19+ mode = "reference",
20+ )
21+ else:
22+ # The release, fetched by digest. This is what makes an APK buildable
23+ # with no jolt-native checkout and no NDK anywhere on the machine.
24+ native.genrule(
25+ name = name,
26+ out = "libvidya.so",
27+ cmd = "cp $(location toolchains//dist:libvidya-android)/libvidya.so \"$OUT\"",
28+ )
new file mode 100644
@@ -0,0 +1,28 @@
1+# Where libvidya comes from, which is a question with two answers.
2+#
3+# A BUCK file cannot branch — `if` outside a `def` is not this dialect — and
4+# this is not a `select` either: it does not vary by configuration but by
5+# whether a checkout is sitting next to this one. So it lives in a macro.
6+def libvidya(name):
7+ """The Android libvidya: a sibling checkout's, or the pinned release's.
8+
9+ Either way the library's bytes are an input to what reads them, which is
10+ the part that matters — an action that shelled out to build it would have
11+ nothing to invalidate on and would serve the same stale object forever.
12+ """
13+ if native.read_root_config("frq", "libvidya", "pinned") == "checkout":
14+ # Staged by the `buck` recipe out of the sibling jolt-native, so that
15+ # anyone working on both repos at once builds what they are editing.
16+ native.export_file(
17+ name = name,
18+ src = "prebuilt/arm64-v8a/libvidya.so",
19+ mode = "reference",
20+ )
21+ else:
22+ # The release, fetched by digest. This is what makes an APK buildable
23+ # with no jolt-native checkout and no NDK anywhere on the machine.
24+ native.genrule(
25+ name = name,
26+ out = "libvidya.so",
27+ cmd = "cp $(location toolchains//dist:libvidya-android)/libvidya.so \"$OUT\"",
28+ )
modified justfile +56 -0
@@ -41,6 +41,62 @@ fetch:
4141 cd {{quote(jolt_native)}} && git checkout --detach {{jolt_native_sha}}; \
4242 fi
4343
44+# buck2, with the machine's paths written where the BUCK files can read them.
45+#
46+# The same arrangement jolt-native uses, and for the same reason: a BUCK file
47+# may not look around the machine, and these five answers differ on every one.
48+# Generated rather than committed, so no checkout carries another's paths.
49+buck *args:
50+ #!/usr/bin/env bash
51+ set -euo pipefail
52+ android_home="${ANDROID_HOME:-$HOME/.local/share/android-sdk}"
53+ chez="${CHEZ_ANDROID:-$HOME/.cache/vidya-chez-android}"
54+ openssl="${OPENSSL_ANDROID:-$HOME/.cache/frq-openssl-android/lib}"
55+ for path in "$android_home/build-tools/36.0.0/aapt2" \
56+ "$android_home/platforms/android-36/android.jar" \
57+ "$chez/boot/tarm64le/scheme.boot" \
58+ "$openssl/libssl.so"; do
59+ [[ -e "$path" ]] || { echo "missing: $path" >&2; exit 1; }
60+ done
61+ # A jolt-native checkout wins over the pinned release, so that anyone
62+ # working on both repos at once builds what they are editing. It is staged
63+ # into this tree because a buck2 cell cannot reach outside its own root,
64+ # and an action that shelled out to the other project would have nothing to
65+ # invalidate on. With no checkout the release answers instead, fetched by
66+ # digest — see toolchains/dist and scripts/libvidya-android.dotslash.
67+ libvidya=pinned
68+ if [[ -d "{{jolt_native}}/crates" ]]; then
69+ libvidya=checkout
70+ ( cd "{{jolt_native}}" && just ffi-android >/dev/null )
71+ mkdir -p android/prebuilt/arm64-v8a
72+ cp "{{jolt_native}}/build/android/arm64-v8a/libvidya.so" \
73+ android/prebuilt/arm64-v8a/libvidya.so
74+ fi
75+ # The boot image's other source roots are outside this cell too; hash them
76+ # here so the digest reaches the action. See android/BUCK.
77+ printf '[frq]\n jolt_native = %s\n android_home = %s\n chez_android = %s\n openssl_android = %s\n libvidya = %s\n boot_stamp = %s\n' \
78+ "{{jolt_native}}" "$android_home" "$chez" "$openssl" "$libvidya" \
79+ "$(android/build-jolt-boot.sh --stamp)" > .buckconfig.local
80+ scripts/buck2 {{args}}
81+
82+# The APK, built as a graph. `just apk install` puts it on the device.
83+apk action="build":
84+ #!/usr/bin/env bash
85+ set -euo pipefail
86+ apk="$(just buck build --show-output //:apk | awk '/frq.apk/{print $2}')"
87+ case "{{action}}" in
88+ build) printf '%s\n' "$apk" ;;
89+ install) "${ADB:-$HOME/.local/share/android-sdk/platform-tools/adb}" install -r "$apk" ;;
90+ run)
91+ adb="${ADB:-$HOME/.local/share/android-sdk/platform-tools/adb}"
92+ "$adb" install -r "$apk"
93+ "$adb" shell am force-stop uk.nandi.frq
94+ "$adb" shell am start -n uk.nandi.frq/.FrqActivity
95+ ;;
96+ log) "${ADB:-$HOME/.local/share/android-sdk/platform-tools/adb}" logcat -s VidyaJolt Vidya ;;
97+ *) echo "usage: just apk [build|install|run|log]" >&2; exit 2 ;;
98+ esac
99+
44100 # The app.
45101 run *args:
46102 LD_LIBRARY_PATH="{{jolt_native}}/target/release" jolt -M:frq {{args}}
@@ -41,6 +41,62 @@ fetch:
41 cd {{quote(jolt_native)}} && git checkout --detach {{jolt_native_sha}}; \41 cd {{quote(jolt_native)}} && git checkout --detach {{jolt_native_sha}}; \
42 fi42 fi
43 43
44+# buck2, with the machine's paths written where the BUCK files can read them.
45+#
46+# The same arrangement jolt-native uses, and for the same reason: a BUCK file
47+# may not look around the machine, and these five answers differ on every one.
48+# Generated rather than committed, so no checkout carries another's paths.
49+buck *args:
50+ #!/usr/bin/env bash
51+ set -euo pipefail
52+ android_home="${ANDROID_HOME:-$HOME/.local/share/android-sdk}"
53+ chez="${CHEZ_ANDROID:-$HOME/.cache/vidya-chez-android}"
54+ openssl="${OPENSSL_ANDROID:-$HOME/.cache/frq-openssl-android/lib}"
55+ for path in "$android_home/build-tools/36.0.0/aapt2" \
56+ "$android_home/platforms/android-36/android.jar" \
57+ "$chez/boot/tarm64le/scheme.boot" \
58+ "$openssl/libssl.so"; do
59+ [[ -e "$path" ]] || { echo "missing: $path" >&2; exit 1; }
60+ done
61+ # A jolt-native checkout wins over the pinned release, so that anyone
62+ # working on both repos at once builds what they are editing. It is staged
63+ # into this tree because a buck2 cell cannot reach outside its own root,
64+ # and an action that shelled out to the other project would have nothing to
65+ # invalidate on. With no checkout the release answers instead, fetched by
66+ # digest — see toolchains/dist and scripts/libvidya-android.dotslash.
67+ libvidya=pinned
68+ if [[ -d "{{jolt_native}}/crates" ]]; then
69+ libvidya=checkout
70+ ( cd "{{jolt_native}}" && just ffi-android >/dev/null )
71+ mkdir -p android/prebuilt/arm64-v8a
72+ cp "{{jolt_native}}/build/android/arm64-v8a/libvidya.so" \
73+ android/prebuilt/arm64-v8a/libvidya.so
74+ fi
75+ # The boot image's other source roots are outside this cell too; hash them
76+ # here so the digest reaches the action. See android/BUCK.
77+ printf '[frq]\n jolt_native = %s\n android_home = %s\n chez_android = %s\n openssl_android = %s\n libvidya = %s\n boot_stamp = %s\n' \
78+ "{{jolt_native}}" "$android_home" "$chez" "$openssl" "$libvidya" \
79+ "$(android/build-jolt-boot.sh --stamp)" > .buckconfig.local
80+ scripts/buck2 {{args}}
81+
82+# The APK, built as a graph. `just apk install` puts it on the device.
83+apk action="build":
84+ #!/usr/bin/env bash
85+ set -euo pipefail
86+ apk="$(just buck build --show-output //:apk | awk '/frq.apk/{print $2}')"
87+ case "{{action}}" in
88+ build) printf '%s\n' "$apk" ;;
89+ install) "${ADB:-$HOME/.local/share/android-sdk/platform-tools/adb}" install -r "$apk" ;;
90+ run)
91+ adb="${ADB:-$HOME/.local/share/android-sdk/platform-tools/adb}"
92+ "$adb" install -r "$apk"
93+ "$adb" shell am force-stop uk.nandi.frq
94+ "$adb" shell am start -n uk.nandi.frq/.FrqActivity
95+ ;;
96+ log) "${ADB:-$HOME/.local/share/android-sdk/platform-tools/adb}" logcat -s VidyaJolt Vidya ;;
97+ *) echo "usage: just apk [build|install|run|log]" >&2; exit 2 ;;
98+ esac
99+
44 # The app.100 # The app.
45 run *args:101 run *args:
46 LD_LIBRARY_PATH="{{jolt_native}}/target/release" jolt -M:frq {{args}}102 LD_LIBRARY_PATH="{{jolt_native}}/target/release" jolt -M:frq {{args}}
added platforms/BUCK +33 -0
new file mode 100644
@@ -0,0 +1,33 @@
1+load("@prelude//platforms:defs.bzl", "host_configuration")
2+load(":defs.bzl", "execution_platform")
3+
4+# Where every action runs: locally, against the BuildBuddy cache. The same
5+# arrangement jolt-native uses, and registered the same two ways — `[build]
6+# execution_platforms` in .buckconfig and `[parser] target_platform_detector_spec`
7+# there too, since setting only the first leaves toolchain-owned actions on the
8+# prelude's uncached default.
9+execution_platform(
10+ name = "cache",
11+ cpu_configuration = host_configuration.cpu,
12+ os_configuration = host_configuration.os,
13+ use_windows_path_separators = host_info().os.is_windows,
14+ visibility = ["PUBLIC"],
15+)
16+
17+# The same platform, allowed to dispatch. Most of this graph cannot use it: the
18+# packaging steps name an Android SDK, a Chez cross build and an OpenSSL by
19+# absolute path, and a worker has none of them. The boot image compile is the
20+# one that could, which is why jolt reaches it through a manifest rather than a
21+# path — see android/BUCK.
22+execution_platform(
23+ name = "remote",
24+ cpu_configuration = host_configuration.cpu,
25+ os_configuration = host_configuration.os,
26+ remote_enabled = True,
27+ remote_execution_properties = {
28+ "OSFamily": "Linux",
29+ "container-image": "docker://gcr.io/flame-public/rbe-ubuntu24-04:latest",
30+ },
31+ use_windows_path_separators = host_info().os.is_windows,
32+ visibility = ["PUBLIC"],
33+)
new file mode 100644
@@ -0,0 +1,33 @@
1+load("@prelude//platforms:defs.bzl", "host_configuration")
2+load(":defs.bzl", "execution_platform")
3+
4+# Where every action runs: locally, against the BuildBuddy cache. The same
5+# arrangement jolt-native uses, and registered the same two ways — `[build]
6+# execution_platforms` in .buckconfig and `[parser] target_platform_detector_spec`
7+# there too, since setting only the first leaves toolchain-owned actions on the
8+# prelude's uncached default.
9+execution_platform(
10+ name = "cache",
11+ cpu_configuration = host_configuration.cpu,
12+ os_configuration = host_configuration.os,
13+ use_windows_path_separators = host_info().os.is_windows,
14+ visibility = ["PUBLIC"],
15+)
16+
17+# The same platform, allowed to dispatch. Most of this graph cannot use it: the
18+# packaging steps name an Android SDK, a Chez cross build and an OpenSSL by
19+# absolute path, and a worker has none of them. The boot image compile is the
20+# one that could, which is why jolt reaches it through a manifest rather than a
21+# path — see android/BUCK.
22+execution_platform(
23+ name = "remote",
24+ cpu_configuration = host_configuration.cpu,
25+ os_configuration = host_configuration.os,
26+ remote_enabled = True,
27+ remote_execution_properties = {
28+ "OSFamily": "Linux",
29+ "container-image": "docker://gcr.io/flame-public/rbe-ubuntu24-04:latest",
30+ },
31+ use_windows_path_separators = host_info().os.is_windows,
32+ visibility = ["PUBLIC"],
33+)
added platforms/defs.bzl +55 -0
new file mode 100644
@@ -0,0 +1,55 @@
1+# The stock prelude//platforms:default with its cache switches flipped on.
2+#
3+# The prelude's own `execution_platform` rule is static Starlark — there is no
4+# buckconfig key that turns caching on for it — so the rule is copied here and
5+# its CommandExecutorConfig changed. Everything else mirrors the original
6+# exactly, so this platform stays configuration-identical to the default.
7+#
8+# Whether actions may run on a worker is an attribute now rather than a
9+# constant. It was false because it had to be: the toolchain named absolute
10+# paths inside this checkout, which no worker would have. Both compilers are
11+# dependencies now, so there is something to ship.
12+load("@prelude//cfg/exec_platform:marker.bzl", "get_exec_platform_marker")
13+
14+def _execution_platform_impl(ctx: AnalysisContext) -> list[Provider]:
15+ constraints = dict()
16+ constraints.update(ctx.attrs.cpu_configuration[ConfigurationInfo].constraints)
17+ constraints.update(ctx.attrs.os_configuration[ConfigurationInfo].constraints)
18+ cfg = ConfigurationInfo(constraints = constraints, values = {})
19+
20+ name = ctx.label.raw_target()
21+ platform = ExecutionPlatformInfo(
22+ label = name,
23+ configuration = cfg,
24+ executor_config = CommandExecutorConfig(
25+ local_enabled = ctx.attrs.local_enabled,
26+ remote_enabled = ctx.attrs.remote_enabled,
27+ remote_cache_enabled = True,
28+ allow_cache_uploads = True,
29+ remote_execution_properties = ctx.attrs.remote_execution_properties,
30+ remote_execution_use_case = "buck2-default",
31+ use_windows_path_separators = ctx.attrs.use_windows_path_separators,
32+ ),
33+ )
34+
35+ return [
36+ DefaultInfo(),
37+ platform,
38+ PlatformInfo(label = str(name), configuration = cfg),
39+ ExecutionPlatformRegistrationInfo(
40+ platforms = [platform],
41+ exec_marker_constraint = get_exec_platform_marker(),
42+ ),
43+ ]
44+
45+execution_platform = rule(
46+ impl = _execution_platform_impl,
47+ attrs = {
48+ "cpu_configuration": attrs.dep(providers = [ConfigurationInfo]),
49+ "local_enabled": attrs.bool(default = True),
50+ "remote_enabled": attrs.bool(default = False),
51+ "remote_execution_properties": attrs.dict(attrs.string(), attrs.string(), default = {}),
52+ "os_configuration": attrs.dep(providers = [ConfigurationInfo]),
53+ "use_windows_path_separators": attrs.bool(),
54+ },
55+)
new file mode 100644
@@ -0,0 +1,55 @@
1+# The stock prelude//platforms:default with its cache switches flipped on.
2+#
3+# The prelude's own `execution_platform` rule is static Starlark — there is no
4+# buckconfig key that turns caching on for it — so the rule is copied here and
5+# its CommandExecutorConfig changed. Everything else mirrors the original
6+# exactly, so this platform stays configuration-identical to the default.
7+#
8+# Whether actions may run on a worker is an attribute now rather than a
9+# constant. It was false because it had to be: the toolchain named absolute
10+# paths inside this checkout, which no worker would have. Both compilers are
11+# dependencies now, so there is something to ship.
12+load("@prelude//cfg/exec_platform:marker.bzl", "get_exec_platform_marker")
13+
14+def _execution_platform_impl(ctx: AnalysisContext) -> list[Provider]:
15+ constraints = dict()
16+ constraints.update(ctx.attrs.cpu_configuration[ConfigurationInfo].constraints)
17+ constraints.update(ctx.attrs.os_configuration[ConfigurationInfo].constraints)
18+ cfg = ConfigurationInfo(constraints = constraints, values = {})
19+
20+ name = ctx.label.raw_target()
21+ platform = ExecutionPlatformInfo(
22+ label = name,
23+ configuration = cfg,
24+ executor_config = CommandExecutorConfig(
25+ local_enabled = ctx.attrs.local_enabled,
26+ remote_enabled = ctx.attrs.remote_enabled,
27+ remote_cache_enabled = True,
28+ allow_cache_uploads = True,
29+ remote_execution_properties = ctx.attrs.remote_execution_properties,
30+ remote_execution_use_case = "buck2-default",
31+ use_windows_path_separators = ctx.attrs.use_windows_path_separators,
32+ ),
33+ )
34+
35+ return [
36+ DefaultInfo(),
37+ platform,
38+ PlatformInfo(label = str(name), configuration = cfg),
39+ ExecutionPlatformRegistrationInfo(
40+ platforms = [platform],
41+ exec_marker_constraint = get_exec_platform_marker(),
42+ ),
43+ ]
44+
45+execution_platform = rule(
46+ impl = _execution_platform_impl,
47+ attrs = {
48+ "cpu_configuration": attrs.dep(providers = [ConfigurationInfo]),
49+ "local_enabled": attrs.bool(default = True),
50+ "remote_enabled": attrs.bool(default = False),
51+ "remote_execution_properties": attrs.dict(attrs.string(), attrs.string(), default = {}),
52+ "os_configuration": attrs.dep(providers = [ConfigurationInfo]),
53+ "use_windows_path_separators": attrs.bool(),
54+ },
55+)
added scripts/BUCK +33 -0
new file mode 100644
@@ -0,0 +1,33 @@
1+# The pins, as artifacts.
2+#
3+# A manifest a build action resolves has to be an input to that action, so that
4+# bumping the version it names invalidates what was built with the old one.
5+# android/BUCK hands this one to the boot image compile along with a DotSlash
6+# to read it with.
7+export_file(
8+ name = "jolt",
9+ mode = "reference",
10+ visibility = ["PUBLIC"],
11+)
12+
13+# The NDK lookup, and the driver that uses it. Inputs, so what links the glue
14+# is named by this repo rather than found next door: the APK's own link step
15+# needs a clang and an llvm-objcopy, and jolt-native is no longer required to
16+# be on the machine for that.
17+export_file(
18+ name = "android-ndk-bin",
19+ mode = "reference",
20+ visibility = ["PUBLIC"],
21+)
22+
23+export_file(
24+ name = "android-cc",
25+ mode = "reference",
26+ visibility = ["PUBLIC"],
27+)
28+
29+export_file(
30+ name = "android-ndk.dotslash",
31+ mode = "reference",
32+ visibility = ["PUBLIC"],
33+)
new file mode 100644
@@ -0,0 +1,33 @@
1+# The pins, as artifacts.
2+#
3+# A manifest a build action resolves has to be an input to that action, so that
4+# bumping the version it names invalidates what was built with the old one.
5+# android/BUCK hands this one to the boot image compile along with a DotSlash
6+# to read it with.
7+export_file(
8+ name = "jolt",
9+ mode = "reference",
10+ visibility = ["PUBLIC"],
11+)
12+
13+# The NDK lookup, and the driver that uses it. Inputs, so what links the glue
14+# is named by this repo rather than found next door: the APK's own link step
15+# needs a clang and an llvm-objcopy, and jolt-native is no longer required to
16+# be on the machine for that.
17+export_file(
18+ name = "android-ndk-bin",
19+ mode = "reference",
20+ visibility = ["PUBLIC"],
21+)
22+
23+export_file(
24+ name = "android-cc",
25+ mode = "reference",
26+ visibility = ["PUBLIC"],
27+)
28+
29+export_file(
30+ name = "android-ndk.dotslash",
31+ mode = "reference",
32+ visibility = ["PUBLIC"],
33+)
added scripts/android-cc +20 -0
new file mode 100755
@@ -0,0 +1,20 @@
1+#!/bin/sh
2+# The NDK's clang, as the linker rustc runs for an Android target.
3+#
4+# The buck-side twin of this is toolchains/ndk.sh, which the C toolchain gets
5+# as a dependency; both resolve the same archive, pinned in one place —
6+# android-ndk.dotslash beside this script. rustc reaches it through a shim
7+# instead because -Clinker takes a plain string: rustc_flags cannot carry an
8+# artifact path, so the linker has to be something with a stable name. The cfg
9+# in toolchains/BUCK is what keeps two NDKs from sharing a cache entry.
10+#
11+# ANDROID_NDK_HOME wins if it is set, so a machine that already has an NDK
12+# skips the download.
13+set -e
14+dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
15+api=${JOLT_ANDROID_API:-28}
16+target=${JOLT_ANDROID_TARGET:-aarch64-linux-android}
17+
18+# android-ndk-bin is where the lookup lives, because an APK build wants the
19+# other binaries out of the same archive.
20+exec "$("$dir/android-ndk-bin")/clang" --target="$target$api" "$@"
new file mode 100755
@@ -0,0 +1,20 @@
1+#!/bin/sh
2+# The NDK's clang, as the linker rustc runs for an Android target.
3+#
4+# The buck-side twin of this is toolchains/ndk.sh, which the C toolchain gets
5+# as a dependency; both resolve the same archive, pinned in one place —
6+# android-ndk.dotslash beside this script. rustc reaches it through a shim
7+# instead because -Clinker takes a plain string: rustc_flags cannot carry an
8+# artifact path, so the linker has to be something with a stable name. The cfg
9+# in toolchains/BUCK is what keeps two NDKs from sharing a cache entry.
10+#
11+# ANDROID_NDK_HOME wins if it is set, so a machine that already has an NDK
12+# skips the download.
13+set -e
14+dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
15+api=${JOLT_ANDROID_API:-28}
16+target=${JOLT_ANDROID_TARGET:-aarch64-linux-android}
17+
18+# android-ndk-bin is where the lookup lives, because an APK build wants the
19+# other binaries out of the same archive.
20+exec "$("$dir/android-ndk-bin")/clang" --target="$target$api" "$@"
added scripts/android-glue.dotslash +66 -0
new file mode 100644
@@ -0,0 +1,66 @@
1+#!/usr/bin/env dotslash
2+
3+// The NativeActivity glue and the ABI's headers, from jolt-native's v0.1.0
4+// release.
5+//
6+// jolt_main.c is what libvidya's android_main dlopens: it boots Chez over the
7+// Jolt image this repo builds and registers every Vidya symbol by hand, since
8+// the loader will not answer for a symbol living in a different shared object.
9+// The headers beside it are what it includes. Both are jolt-native's; only the
10+// boot image is frq's.
11+//
12+// Source, not a binary, and pinned for the same reason the library is: an APK
13+// should not need a second checkout to build.
14+{
15+ "name": "android-glue",
16+ "platforms": {
17+ "linux-x86_64": {
18+ "size": 6685,
19+ "hash": "sha256",
20+ "digest": "8e9c5bae9c9919354430c9d3e888e6c3430d26520f9f429a7d2de2b850481133",
21+ "format": "tar.gz",
22+ "path": "jolt-native-android-glue/android/jolt_main.c",
23+ "providers": [
24+ {
25+ "url": "https://gitlab.com/-/project/85910092/uploads/16d2fe6e8df490940ca0a2a18ba4c092/jolt-native-android-glue-v0.1.0.tar.gz"
26+ }
27+ ]
28+ },
29+ "linux-aarch64": {
30+ "size": 6685,
31+ "hash": "sha256",
32+ "digest": "8e9c5bae9c9919354430c9d3e888e6c3430d26520f9f429a7d2de2b850481133",
33+ "format": "tar.gz",
34+ "path": "jolt-native-android-glue/android/jolt_main.c",
35+ "providers": [
36+ {
37+ "url": "https://gitlab.com/-/project/85910092/uploads/16d2fe6e8df490940ca0a2a18ba4c092/jolt-native-android-glue-v0.1.0.tar.gz"
38+ }
39+ ]
40+ },
41+ "macos-x86_64": {
42+ "size": 6685,
43+ "hash": "sha256",
44+ "digest": "8e9c5bae9c9919354430c9d3e888e6c3430d26520f9f429a7d2de2b850481133",
45+ "format": "tar.gz",
46+ "path": "jolt-native-android-glue/android/jolt_main.c",
47+ "providers": [
48+ {
49+ "url": "https://gitlab.com/-/project/85910092/uploads/16d2fe6e8df490940ca0a2a18ba4c092/jolt-native-android-glue-v0.1.0.tar.gz"
50+ }
51+ ]
52+ },
53+ "macos-aarch64": {
54+ "size": 6685,
55+ "hash": "sha256",
56+ "digest": "8e9c5bae9c9919354430c9d3e888e6c3430d26520f9f429a7d2de2b850481133",
57+ "format": "tar.gz",
58+ "path": "jolt-native-android-glue/android/jolt_main.c",
59+ "providers": [
60+ {
61+ "url": "https://gitlab.com/-/project/85910092/uploads/16d2fe6e8df490940ca0a2a18ba4c092/jolt-native-android-glue-v0.1.0.tar.gz"
62+ }
63+ ]
64+ }
65+ }
66+}
new file mode 100644
@@ -0,0 +1,66 @@
1+#!/usr/bin/env dotslash
2+
3+// The NativeActivity glue and the ABI's headers, from jolt-native's v0.1.0
4+// release.
5+//
6+// jolt_main.c is what libvidya's android_main dlopens: it boots Chez over the
7+// Jolt image this repo builds and registers every Vidya symbol by hand, since
8+// the loader will not answer for a symbol living in a different shared object.
9+// The headers beside it are what it includes. Both are jolt-native's; only the
10+// boot image is frq's.
11+//
12+// Source, not a binary, and pinned for the same reason the library is: an APK
13+// should not need a second checkout to build.
14+{
15+ "name": "android-glue",
16+ "platforms": {
17+ "linux-x86_64": {
18+ "size": 6685,
19+ "hash": "sha256",
20+ "digest": "8e9c5bae9c9919354430c9d3e888e6c3430d26520f9f429a7d2de2b850481133",
21+ "format": "tar.gz",
22+ "path": "jolt-native-android-glue/android/jolt_main.c",
23+ "providers": [
24+ {
25+ "url": "https://gitlab.com/-/project/85910092/uploads/16d2fe6e8df490940ca0a2a18ba4c092/jolt-native-android-glue-v0.1.0.tar.gz"
26+ }
27+ ]
28+ },
29+ "linux-aarch64": {
30+ "size": 6685,
31+ "hash": "sha256",
32+ "digest": "8e9c5bae9c9919354430c9d3e888e6c3430d26520f9f429a7d2de2b850481133",
33+ "format": "tar.gz",
34+ "path": "jolt-native-android-glue/android/jolt_main.c",
35+ "providers": [
36+ {
37+ "url": "https://gitlab.com/-/project/85910092/uploads/16d2fe6e8df490940ca0a2a18ba4c092/jolt-native-android-glue-v0.1.0.tar.gz"
38+ }
39+ ]
40+ },
41+ "macos-x86_64": {
42+ "size": 6685,
43+ "hash": "sha256",
44+ "digest": "8e9c5bae9c9919354430c9d3e888e6c3430d26520f9f429a7d2de2b850481133",
45+ "format": "tar.gz",
46+ "path": "jolt-native-android-glue/android/jolt_main.c",
47+ "providers": [
48+ {
49+ "url": "https://gitlab.com/-/project/85910092/uploads/16d2fe6e8df490940ca0a2a18ba4c092/jolt-native-android-glue-v0.1.0.tar.gz"
50+ }
51+ ]
52+ },
53+ "macos-aarch64": {
54+ "size": 6685,
55+ "hash": "sha256",
56+ "digest": "8e9c5bae9c9919354430c9d3e888e6c3430d26520f9f429a7d2de2b850481133",
57+ "format": "tar.gz",
58+ "path": "jolt-native-android-glue/android/jolt_main.c",
59+ "providers": [
60+ {
61+ "url": "https://gitlab.com/-/project/85910092/uploads/16d2fe6e8df490940ca0a2a18ba4c092/jolt-native-android-glue-v0.1.0.tar.gz"
62+ }
63+ ]
64+ }
65+ }
66+}
added scripts/android-ndk-bin +23 -0
new file mode 100755
@@ -0,0 +1,23 @@
1+#!/bin/sh
2+# Print the directory holding the NDK's clang and LLVM binutils.
3+#
4+# android-cc needs one of those binaries and an APK build needs others —
5+# llvm-objcopy, for the boot image blob — so the lookup is here rather than
6+# duplicated at each caller. ANDROID_NDK_HOME wins if it is set; otherwise the
7+# pin in android-ndk.dotslash beside this script answers, downloading once.
8+set -e
9+dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
10+
11+ndk=${ANDROID_NDK_HOME:-${ANDROID_NDK_ROOT:-}}
12+if [ -n "$ndk" ]; then
13+ for host in linux-x86_64 darwin-x86_64; do
14+ if [ -d "$ndk/toolchains/llvm/prebuilt/$host/bin" ]; then
15+ echo "$ndk/toolchains/llvm/prebuilt/$host/bin"
16+ exit 0
17+ fi
18+ done
19+ echo "scripts/android-ndk-bin: no llvm prebuilt under $ndk" >&2
20+ exit 1
21+fi
22+
23+dirname -- "$(dotslash -- fetch "$dir/android-ndk.dotslash")"
new file mode 100755
@@ -0,0 +1,23 @@
1+#!/bin/sh
2+# Print the directory holding the NDK's clang and LLVM binutils.
3+#
4+# android-cc needs one of those binaries and an APK build needs others —
5+# llvm-objcopy, for the boot image blob — so the lookup is here rather than
6+# duplicated at each caller. ANDROID_NDK_HOME wins if it is set; otherwise the
7+# pin in android-ndk.dotslash beside this script answers, downloading once.
8+set -e
9+dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
10+
11+ndk=${ANDROID_NDK_HOME:-${ANDROID_NDK_ROOT:-}}
12+if [ -n "$ndk" ]; then
13+ for host in linux-x86_64 darwin-x86_64; do
14+ if [ -d "$ndk/toolchains/llvm/prebuilt/$host/bin" ]; then
15+ echo "$ndk/toolchains/llvm/prebuilt/$host/bin"
16+ exit 0
17+ fi
18+ done
19+ echo "scripts/android-ndk-bin: no llvm prebuilt under $ndk" >&2
20+ exit 1
21+fi
22+
23+dirname -- "$(dotslash -- fetch "$dir/android-ndk.dotslash")"
added scripts/android-ndk.dotslash +55 -0
new file mode 100755
@@ -0,0 +1,55 @@
1+#!/usr/bin/env dotslash
2+
3+// The NDK, pinned the way every other tool here is.
4+//
5+// bin/clang is the path named, but the whole archive is what matters: the
6+// driver finds its sysroot, its resource directory and the rest of the LLVM
7+// binaries relative to itself, so the tree has to travel intact. The name is
8+// only what makes this a runnable shim as well as a manifest.
9+//
10+// Google publishes SHA-1 for these and nothing else, so the digests below were
11+// taken from the archives and checked against the SHA-1 in repository2-3.xml.
12+//
13+// There is no Linux/arm64 NDK. Both macOS entries are the x86_64 prebuilt,
14+// which is what Google ships an arm64 host runs it under Rosetta.
15+{
16+ "name": "android-ndk-r29",
17+ "platforms": {
18+ "linux-x86_64": {
19+ "size": 783549481,
20+ "hash": "sha256",
21+ "digest": "4abbbcdc842f3d4879206e9695d52709603e52dd68d3c1fff04b3b5e7a308ecf",
22+ "format": "zip",
23+ "path": "android-ndk-r29/toolchains/llvm/prebuilt/linux-x86_64/bin/clang",
24+ "providers": [
25+ {
26+ "url": "https://dl.google.com/android/repository/android-ndk-r29-linux.zip"
27+ }
28+ ]
29+ },
30+ "macos-x86_64": {
31+ "size": 1049519838,
32+ "hash": "sha256",
33+ "digest": "ce5e4b100ec5fe5be4eb3edcb2c02528824ff9cda3860f5304619be6c3da34d3",
34+ "format": "zip",
35+ "path": "android-ndk-r29/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang",
36+ "providers": [
37+ {
38+ "url": "https://dl.google.com/android/repository/android-ndk-r29-darwin.zip"
39+ }
40+ ]
41+ },
42+ "macos-aarch64": {
43+ "size": 1049519838,
44+ "hash": "sha256",
45+ "digest": "ce5e4b100ec5fe5be4eb3edcb2c02528824ff9cda3860f5304619be6c3da34d3",
46+ "format": "zip",
47+ "path": "android-ndk-r29/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang",
48+ "providers": [
49+ {
50+ "url": "https://dl.google.com/android/repository/android-ndk-r29-darwin.zip"
51+ }
52+ ]
53+ }
54+ }
55+}
new file mode 100755
@@ -0,0 +1,55 @@
1+#!/usr/bin/env dotslash
2+
3+// The NDK, pinned the way every other tool here is.
4+//
5+// bin/clang is the path named, but the whole archive is what matters: the
6+// driver finds its sysroot, its resource directory and the rest of the LLVM
7+// binaries relative to itself, so the tree has to travel intact. The name is
8+// only what makes this a runnable shim as well as a manifest.
9+//
10+// Google publishes SHA-1 for these and nothing else, so the digests below were
11+// taken from the archives and checked against the SHA-1 in repository2-3.xml.
12+//
13+// There is no Linux/arm64 NDK. Both macOS entries are the x86_64 prebuilt,
14+// which is what Google ships an arm64 host runs it under Rosetta.
15+{
16+ "name": "android-ndk-r29",
17+ "platforms": {
18+ "linux-x86_64": {
19+ "size": 783549481,
20+ "hash": "sha256",
21+ "digest": "4abbbcdc842f3d4879206e9695d52709603e52dd68d3c1fff04b3b5e7a308ecf",
22+ "format": "zip",
23+ "path": "android-ndk-r29/toolchains/llvm/prebuilt/linux-x86_64/bin/clang",
24+ "providers": [
25+ {
26+ "url": "https://dl.google.com/android/repository/android-ndk-r29-linux.zip"
27+ }
28+ ]
29+ },
30+ "macos-x86_64": {
31+ "size": 1049519838,
32+ "hash": "sha256",
33+ "digest": "ce5e4b100ec5fe5be4eb3edcb2c02528824ff9cda3860f5304619be6c3da34d3",
34+ "format": "zip",
35+ "path": "android-ndk-r29/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang",
36+ "providers": [
37+ {
38+ "url": "https://dl.google.com/android/repository/android-ndk-r29-darwin.zip"
39+ }
40+ ]
41+ },
42+ "macos-aarch64": {
43+ "size": 1049519838,
44+ "hash": "sha256",
45+ "digest": "ce5e4b100ec5fe5be4eb3edcb2c02528824ff9cda3860f5304619be6c3da34d3",
46+ "format": "zip",
47+ "path": "android-ndk-r29/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang",
48+ "providers": [
49+ {
50+ "url": "https://dl.google.com/android/repository/android-ndk-r29-darwin.zip"
51+ }
52+ ]
53+ }
54+ }
55+}
added scripts/buck2 +67 -0
new file mode 100755
@@ -0,0 +1,67 @@
1+#!/usr/bin/env dotslash
2+
3+{
4+ "name": "buck2",
5+ "platforms": {
6+ "linux-x86_64": {
7+ "size": 39303524,
8+ "hash": "blake3",
9+ "digest": "afff180bc257de220f393ed4551693d877849cda012ac39cf9d4f7a27693848e",
10+ "format": "zst",
11+ "path": "buck2",
12+ "providers": [
13+ {
14+ "url": "https://github.com/facebook/buck2/releases/download/2026-08-22/buck2-x86_64-unknown-linux-musl.zst"
15+ }
16+ ]
17+ },
18+ "linux-aarch64": {
19+ "size": 37790120,
20+ "hash": "blake3",
21+ "digest": "52e2182d65630a0b68bb33ca9ae72b64c028668e5d02fe5fd46d8fcb3c361918",
22+ "format": "zst",
23+ "path": "buck2",
24+ "providers": [
25+ {
26+ "url": "https://github.com/facebook/buck2/releases/download/2026-08-22/buck2-aarch64-unknown-linux-musl.zst"
27+ }
28+ ]
29+ },
30+ "macos-x86_64": {
31+ "size": 36655230,
32+ "hash": "blake3",
33+ "digest": "1959b2f85dbba714f5d1f7824e813b0fca81224117a51cec7db17b8ae9fc1012",
34+ "format": "zst",
35+ "path": "buck2",
36+ "providers": [
37+ {
38+ "url": "https://github.com/facebook/buck2/releases/download/2026-08-22/buck2-x86_64-apple-darwin.zst"
39+ }
40+ ]
41+ },
42+ "macos-aarch64": {
43+ "size": 34435267,
44+ "hash": "blake3",
45+ "digest": "15ff15f1c10b8ddb3e8b3d43ca73964dec3719a514d805119c5e5963257daefe",
46+ "format": "zst",
47+ "path": "buck2",
48+ "providers": [
49+ {
50+ "url": "https://github.com/facebook/buck2/releases/download/2026-08-22/buck2-aarch64-apple-darwin.zst"
51+ }
52+ ]
53+ },
54+ "windows-x86_64": {
55+ "size": 31803375,
56+ "hash": "blake3",
57+ "digest": "ab36fa3ee1b4fe2e597b8bf0bfa27f815b64ff3f520b6456fe67cb48b24f85e6",
58+ "format": "zst",
59+ "path": "buck2.exe",
60+ "providers": [
61+ {
62+ "url": "https://github.com/facebook/buck2/releases/download/2026-08-22/buck2-x86_64-pc-windows-msvc.exe.zst"
63+ }
64+ ]
65+ }
66+ }
67+}
new file mode 100755
@@ -0,0 +1,67 @@
1+#!/usr/bin/env dotslash
2+
3+{
4+ "name": "buck2",
5+ "platforms": {
6+ "linux-x86_64": {
7+ "size": 39303524,
8+ "hash": "blake3",
9+ "digest": "afff180bc257de220f393ed4551693d877849cda012ac39cf9d4f7a27693848e",
10+ "format": "zst",
11+ "path": "buck2",
12+ "providers": [
13+ {
14+ "url": "https://github.com/facebook/buck2/releases/download/2026-08-22/buck2-x86_64-unknown-linux-musl.zst"
15+ }
16+ ]
17+ },
18+ "linux-aarch64": {
19+ "size": 37790120,
20+ "hash": "blake3",
21+ "digest": "52e2182d65630a0b68bb33ca9ae72b64c028668e5d02fe5fd46d8fcb3c361918",
22+ "format": "zst",
23+ "path": "buck2",
24+ "providers": [
25+ {
26+ "url": "https://github.com/facebook/buck2/releases/download/2026-08-22/buck2-aarch64-unknown-linux-musl.zst"
27+ }
28+ ]
29+ },
30+ "macos-x86_64": {
31+ "size": 36655230,
32+ "hash": "blake3",
33+ "digest": "1959b2f85dbba714f5d1f7824e813b0fca81224117a51cec7db17b8ae9fc1012",
34+ "format": "zst",
35+ "path": "buck2",
36+ "providers": [
37+ {
38+ "url": "https://github.com/facebook/buck2/releases/download/2026-08-22/buck2-x86_64-apple-darwin.zst"
39+ }
40+ ]
41+ },
42+ "macos-aarch64": {
43+ "size": 34435267,
44+ "hash": "blake3",
45+ "digest": "15ff15f1c10b8ddb3e8b3d43ca73964dec3719a514d805119c5e5963257daefe",
46+ "format": "zst",
47+ "path": "buck2",
48+ "providers": [
49+ {
50+ "url": "https://github.com/facebook/buck2/releases/download/2026-08-22/buck2-aarch64-apple-darwin.zst"
51+ }
52+ ]
53+ },
54+ "windows-x86_64": {
55+ "size": 31803375,
56+ "hash": "blake3",
57+ "digest": "ab36fa3ee1b4fe2e597b8bf0bfa27f815b64ff3f520b6456fe67cb48b24f85e6",
58+ "format": "zst",
59+ "path": "buck2.exe",
60+ "providers": [
61+ {
62+ "url": "https://github.com/facebook/buck2/releases/download/2026-08-22/buck2-x86_64-pc-windows-msvc.exe.zst"
63+ }
64+ ]
65+ }
66+ }
67+}
added scripts/dotslash-to-buck +90 -0
new file mode 100755
@@ -0,0 +1,90 @@
1+#!/usr/bin/env python3
2+"""Generate buck2 http_archive data from the DotSlash files beside this script.
3+
4+DotSlash is where a tool's version, URL and digest are written down. buck needs
5+the same three facts to fetch that tool itself — it cannot use the shim, which
6+resolves on the machine at run time and so is neither an action input nor
7+anything a remote worker could be given. Rather than write them twice and let
8+them drift, this reads the manifests and emits the table buck reads.
9+
10+ just sync-dist # after editing any scripts/*.dotslash
11+
12+The DotSlash format is JSON with a `//` shebang line on top: one entry per
13+platform, each with a url, a size, a digest and the path of the binary inside
14+the archive. Only the archive itself is wanted here; the path inside it is the
15+shim's business, and the toolchain rules name what they need.
16+"""
17+
18+import json
19+import pathlib
20+import sys
21+
22+HERE = pathlib.Path(__file__).resolve().parent
23+OUT = HERE.parent / "toolchains" / "dist" / "generated.bzl"
24+
25+# The tools buck fetches for itself. buck2 is not among them: it is the thing
26+# doing the fetching. jolt is not either — it is fetched by DotSlash on the
27+# machine that runs the boot image compile, which may not be this one.
28+TOOLS = {
29+ # DotSlash itself, so an action can fetch what it needs where it runs
30+ # rather than being handed it from here. See android/BUCK.
31+ "dotslash": "dotslash",
32+ # jolt-native's release, so the APK's UI half and the Jolt side of the
33+ # tree ABI are fetched rather than built out of a second checkout.
34+ "libvidya-android": "libvidya-android",
35+ "glimmer-vidya": "glimmer-vidya",
36+ "android-glue": "android-glue",
37+}
38+
39+
40+def load(path):
41+ text = path.read_text()
42+ return json.loads(text[text.index("{") :])
43+
44+
45+def main():
46+ entries = {}
47+ for stem, name in sorted(TOOLS.items()):
48+ for candidate in (HERE / f"{stem}.dotslash", HERE / stem):
49+ if candidate.exists():
50+ manifest = load(candidate)
51+ break
52+ else:
53+ sys.exit(f"no DotSlash manifest for {stem}")
54+
55+ platforms = {}
56+ for platform, entry in sorted(manifest["platforms"].items()):
57+ if entry["hash"] != "sha256":
58+ # http_archive takes sha256 and nothing else.
59+ continue
60+ url = entry["providers"][0]["url"]
61+ # The path of the binary inside the archive starts with the
62+ # directory the tarball unpacks into, which is what to strip.
63+ # A flat archive — one binary, no directory around it — has
64+ # nothing to strip, and naming the binary would strip the whole
65+ # payload.
66+ head, _, rest = entry["path"].partition("/")
67+ platforms[platform] = {
68+ "url": url,
69+ "sha256": entry["digest"],
70+ "strip_prefix": head if rest else "",
71+ "type": entry["format"],
72+ }
73+ entries[name] = platforms
74+
75+ lines = [
76+ "# @generated by scripts/dotslash-to-buck — do not edit.",
77+ "#",
78+ "# The same archives scripts/*.dotslash pins, as facts buck can act on.",
79+ "# Bump the DotSlash file and run `just sync-dist`.",
80+ "",
81+ "DIST = " + json.dumps(entries, indent=4).replace(": true", ": True"),
82+ "",
83+ ]
84+ OUT.parent.mkdir(parents=True, exist_ok=True)
85+ OUT.write_text("\n".join(lines))
86+ print(f"wrote {OUT.relative_to(HERE.parent)}")
87+
88+
89+if __name__ == "__main__":
90+ main()
new file mode 100755
@@ -0,0 +1,90 @@
1+#!/usr/bin/env python3
2+"""Generate buck2 http_archive data from the DotSlash files beside this script.
3+
4+DotSlash is where a tool's version, URL and digest are written down. buck needs
5+the same three facts to fetch that tool itself — it cannot use the shim, which
6+resolves on the machine at run time and so is neither an action input nor
7+anything a remote worker could be given. Rather than write them twice and let
8+them drift, this reads the manifests and emits the table buck reads.
9+
10+ just sync-dist # after editing any scripts/*.dotslash
11+
12+The DotSlash format is JSON with a `//` shebang line on top: one entry per
13+platform, each with a url, a size, a digest and the path of the binary inside
14+the archive. Only the archive itself is wanted here; the path inside it is the
15+shim's business, and the toolchain rules name what they need.
16+"""
17+
18+import json
19+import pathlib
20+import sys
21+
22+HERE = pathlib.Path(__file__).resolve().parent
23+OUT = HERE.parent / "toolchains" / "dist" / "generated.bzl"
24+
25+# The tools buck fetches for itself. buck2 is not among them: it is the thing
26+# doing the fetching. jolt is not either — it is fetched by DotSlash on the
27+# machine that runs the boot image compile, which may not be this one.
28+TOOLS = {
29+ # DotSlash itself, so an action can fetch what it needs where it runs
30+ # rather than being handed it from here. See android/BUCK.
31+ "dotslash": "dotslash",
32+ # jolt-native's release, so the APK's UI half and the Jolt side of the
33+ # tree ABI are fetched rather than built out of a second checkout.
34+ "libvidya-android": "libvidya-android",
35+ "glimmer-vidya": "glimmer-vidya",
36+ "android-glue": "android-glue",
37+}
38+
39+
40+def load(path):
41+ text = path.read_text()
42+ return json.loads(text[text.index("{") :])
43+
44+
45+def main():
46+ entries = {}
47+ for stem, name in sorted(TOOLS.items()):
48+ for candidate in (HERE / f"{stem}.dotslash", HERE / stem):
49+ if candidate.exists():
50+ manifest = load(candidate)
51+ break
52+ else:
53+ sys.exit(f"no DotSlash manifest for {stem}")
54+
55+ platforms = {}
56+ for platform, entry in sorted(manifest["platforms"].items()):
57+ if entry["hash"] != "sha256":
58+ # http_archive takes sha256 and nothing else.
59+ continue
60+ url = entry["providers"][0]["url"]
61+ # The path of the binary inside the archive starts with the
62+ # directory the tarball unpacks into, which is what to strip.
63+ # A flat archive — one binary, no directory around it — has
64+ # nothing to strip, and naming the binary would strip the whole
65+ # payload.
66+ head, _, rest = entry["path"].partition("/")
67+ platforms[platform] = {
68+ "url": url,
69+ "sha256": entry["digest"],
70+ "strip_prefix": head if rest else "",
71+ "type": entry["format"],
72+ }
73+ entries[name] = platforms
74+
75+ lines = [
76+ "# @generated by scripts/dotslash-to-buck — do not edit.",
77+ "#",
78+ "# The same archives scripts/*.dotslash pins, as facts buck can act on.",
79+ "# Bump the DotSlash file and run `just sync-dist`.",
80+ "",
81+ "DIST = " + json.dumps(entries, indent=4).replace(": true", ": True"),
82+ "",
83+ ]
84+ OUT.parent.mkdir(parents=True, exist_ok=True)
85+ OUT.write_text("\n".join(lines))
86+ print(f"wrote {OUT.relative_to(HERE.parent)}")
87+
88+
89+if __name__ == "__main__":
90+ main()
added scripts/dotslash.dotslash +66 -0
new file mode 100644
@@ -0,0 +1,66 @@
1+#!/usr/bin/env dotslash
2+
3+// DotSlash itself, so that a remote worker can fetch a toolchain the way this
4+// machine does.
5+//
6+// The other manifests here are pins buck reads and then fetches for itself.
7+// This one is different: it is fetched so that it can do the fetching. An NDK
8+// is three gigabytes unpacked, and shipping that to a worker as an action
9+// input means uploading it to the CAS first; a worker that runs this instead
10+// downloads the same pinned archive, by the same digest, from Google.
11+//
12+// Not runnable as a shim, and nothing tries: a DotSlash file cannot bootstrap
13+// the program that reads it.
14+{
15+ "name": "dotslash",
16+ "platforms": {
17+ "linux-x86_64": {
18+ "size": 842816,
19+ "hash": "sha256",
20+ "digest": "4c75c6eb7890ae35993b962073f6d9bbe78b42b81a5691303ad70f63bfbf7196",
21+ "format": "tar.gz",
22+ "path": "dotslash",
23+ "providers": [
24+ {
25+ "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-linux-musl.x86_64.v0.5.9.tar.gz"
26+ }
27+ ]
28+ },
29+ "linux-aarch64": {
30+ "size": 754251,
31+ "hash": "sha256",
32+ "digest": "e58374446dacd4a228388cd275d974bbd8f946916b513cf0eb86bdff5d9d675e",
33+ "format": "tar.gz",
34+ "path": "dotslash",
35+ "providers": [
36+ {
37+ "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-linux-musl.arm64.v0.5.9.tar.gz"
38+ }
39+ ]
40+ },
41+ "macos-x86_64": {
42+ "size": 716580,
43+ "hash": "sha256",
44+ "digest": "51446593596ffe4cd22bc43a0fe6d82dd367758eb53cb4ade36d6a684ed08469",
45+ "format": "tar.gz",
46+ "path": "dotslash",
47+ "providers": [
48+ {
49+ "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-macos-amd64.v0.5.9.tar.gz"
50+ }
51+ ]
52+ },
53+ "macos-aarch64": {
54+ "size": 600404,
55+ "hash": "sha256",
56+ "digest": "eef15be1b554de7b1409cb0c69c24f2cd04f19c0f101ad69fcdf9d66d4d51639",
57+ "format": "tar.gz",
58+ "path": "dotslash",
59+ "providers": [
60+ {
61+ "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-macos-arm64.v0.5.9.tar.gz"
62+ }
63+ ]
64+ }
65+ }
66+}
new file mode 100644
@@ -0,0 +1,66 @@
1+#!/usr/bin/env dotslash
2+
3+// DotSlash itself, so that a remote worker can fetch a toolchain the way this
4+// machine does.
5+//
6+// The other manifests here are pins buck reads and then fetches for itself.
7+// This one is different: it is fetched so that it can do the fetching. An NDK
8+// is three gigabytes unpacked, and shipping that to a worker as an action
9+// input means uploading it to the CAS first; a worker that runs this instead
10+// downloads the same pinned archive, by the same digest, from Google.
11+//
12+// Not runnable as a shim, and nothing tries: a DotSlash file cannot bootstrap
13+// the program that reads it.
14+{
15+ "name": "dotslash",
16+ "platforms": {
17+ "linux-x86_64": {
18+ "size": 842816,
19+ "hash": "sha256",
20+ "digest": "4c75c6eb7890ae35993b962073f6d9bbe78b42b81a5691303ad70f63bfbf7196",
21+ "format": "tar.gz",
22+ "path": "dotslash",
23+ "providers": [
24+ {
25+ "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-linux-musl.x86_64.v0.5.9.tar.gz"
26+ }
27+ ]
28+ },
29+ "linux-aarch64": {
30+ "size": 754251,
31+ "hash": "sha256",
32+ "digest": "e58374446dacd4a228388cd275d974bbd8f946916b513cf0eb86bdff5d9d675e",
33+ "format": "tar.gz",
34+ "path": "dotslash",
35+ "providers": [
36+ {
37+ "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-linux-musl.arm64.v0.5.9.tar.gz"
38+ }
39+ ]
40+ },
41+ "macos-x86_64": {
42+ "size": 716580,
43+ "hash": "sha256",
44+ "digest": "51446593596ffe4cd22bc43a0fe6d82dd367758eb53cb4ade36d6a684ed08469",
45+ "format": "tar.gz",
46+ "path": "dotslash",
47+ "providers": [
48+ {
49+ "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-macos-amd64.v0.5.9.tar.gz"
50+ }
51+ ]
52+ },
53+ "macos-aarch64": {
54+ "size": 600404,
55+ "hash": "sha256",
56+ "digest": "eef15be1b554de7b1409cb0c69c24f2cd04f19c0f101ad69fcdf9d66d4d51639",
57+ "format": "tar.gz",
58+ "path": "dotslash",
59+ "providers": [
60+ {
61+ "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-macos-arm64.v0.5.9.tar.gz"
62+ }
63+ ]
64+ }
65+ }
66+}
added scripts/glimmer-vidya.dotslash +62 -0
new file mode 100755
@@ -0,0 +1,62 @@
1+#!/usr/bin/env dotslash
2+
3+// The Jolt side that binds the tree ABI, from jolt-native's v0.1.0 release.
4+//
5+// deps.edn takes this as a git dependency, and jolt resolves it into its own
6+// cache. The boot image cannot: a cross compile has no dependency resolution,
7+// so every source root has to be named, and a path into somebody's ~/.jolt is
8+// not a thing a build can be handed. This is the same tree at the same commit,
9+// as an archive an action can depend on.
10+{
11+ "name": "glimmer-vidya",
12+ "platforms": {
13+ "linux-x86_64": {
14+ "size": 17964,
15+ "hash": "sha256",
16+ "digest": "3842a5981c0656eb14915aead80814286040c24e83732d5e69d4f4470308fbf5",
17+ "format": "tar.gz",
18+ "path": "glimmer-vidya/deps.edn",
19+ "providers": [
20+ {
21+ "url": "https://gitlab.com/-/project/85910092/uploads/3448c1fde6a113a5578335ac54811d0f/jolt-native-glimmer-vidya-v0.1.0.tar.gz"
22+ }
23+ ]
24+ },
25+ "linux-aarch64": {
26+ "size": 17964,
27+ "hash": "sha256",
28+ "digest": "3842a5981c0656eb14915aead80814286040c24e83732d5e69d4f4470308fbf5",
29+ "format": "tar.gz",
30+ "path": "glimmer-vidya/deps.edn",
31+ "providers": [
32+ {
33+ "url": "https://gitlab.com/-/project/85910092/uploads/3448c1fde6a113a5578335ac54811d0f/jolt-native-glimmer-vidya-v0.1.0.tar.gz"
34+ }
35+ ]
36+ },
37+ "macos-x86_64": {
38+ "size": 17964,
39+ "hash": "sha256",
40+ "digest": "3842a5981c0656eb14915aead80814286040c24e83732d5e69d4f4470308fbf5",
41+ "format": "tar.gz",
42+ "path": "glimmer-vidya/deps.edn",
43+ "providers": [
44+ {
45+ "url": "https://gitlab.com/-/project/85910092/uploads/3448c1fde6a113a5578335ac54811d0f/jolt-native-glimmer-vidya-v0.1.0.tar.gz"
46+ }
47+ ]
48+ },
49+ "macos-aarch64": {
50+ "size": 17964,
51+ "hash": "sha256",
52+ "digest": "3842a5981c0656eb14915aead80814286040c24e83732d5e69d4f4470308fbf5",
53+ "format": "tar.gz",
54+ "path": "glimmer-vidya/deps.edn",
55+ "providers": [
56+ {
57+ "url": "https://gitlab.com/-/project/85910092/uploads/3448c1fde6a113a5578335ac54811d0f/jolt-native-glimmer-vidya-v0.1.0.tar.gz"
58+ }
59+ ]
60+ }
61+ }
62+}
new file mode 100755
@@ -0,0 +1,62 @@
1+#!/usr/bin/env dotslash
2+
3+// The Jolt side that binds the tree ABI, from jolt-native's v0.1.0 release.
4+//
5+// deps.edn takes this as a git dependency, and jolt resolves it into its own
6+// cache. The boot image cannot: a cross compile has no dependency resolution,
7+// so every source root has to be named, and a path into somebody's ~/.jolt is
8+// not a thing a build can be handed. This is the same tree at the same commit,
9+// as an archive an action can depend on.
10+{
11+ "name": "glimmer-vidya",
12+ "platforms": {
13+ "linux-x86_64": {
14+ "size": 17964,
15+ "hash": "sha256",
16+ "digest": "3842a5981c0656eb14915aead80814286040c24e83732d5e69d4f4470308fbf5",
17+ "format": "tar.gz",
18+ "path": "glimmer-vidya/deps.edn",
19+ "providers": [
20+ {
21+ "url": "https://gitlab.com/-/project/85910092/uploads/3448c1fde6a113a5578335ac54811d0f/jolt-native-glimmer-vidya-v0.1.0.tar.gz"
22+ }
23+ ]
24+ },
25+ "linux-aarch64": {
26+ "size": 17964,
27+ "hash": "sha256",
28+ "digest": "3842a5981c0656eb14915aead80814286040c24e83732d5e69d4f4470308fbf5",
29+ "format": "tar.gz",
30+ "path": "glimmer-vidya/deps.edn",
31+ "providers": [
32+ {
33+ "url": "https://gitlab.com/-/project/85910092/uploads/3448c1fde6a113a5578335ac54811d0f/jolt-native-glimmer-vidya-v0.1.0.tar.gz"
34+ }
35+ ]
36+ },
37+ "macos-x86_64": {
38+ "size": 17964,
39+ "hash": "sha256",
40+ "digest": "3842a5981c0656eb14915aead80814286040c24e83732d5e69d4f4470308fbf5",
41+ "format": "tar.gz",
42+ "path": "glimmer-vidya/deps.edn",
43+ "providers": [
44+ {
45+ "url": "https://gitlab.com/-/project/85910092/uploads/3448c1fde6a113a5578335ac54811d0f/jolt-native-glimmer-vidya-v0.1.0.tar.gz"
46+ }
47+ ]
48+ },
49+ "macos-aarch64": {
50+ "size": 17964,
51+ "hash": "sha256",
52+ "digest": "3842a5981c0656eb14915aead80814286040c24e83732d5e69d4f4470308fbf5",
53+ "format": "tar.gz",
54+ "path": "glimmer-vidya/deps.edn",
55+ "providers": [
56+ {
57+ "url": "https://gitlab.com/-/project/85910092/uploads/3448c1fde6a113a5578335ac54811d0f/jolt-native-glimmer-vidya-v0.1.0.tar.gz"
58+ }
59+ ]
60+ }
61+ }
62+}
added scripts/libvidya-android.dotslash +64 -0
new file mode 100755
@@ -0,0 +1,64 @@
1+#!/usr/bin/env dotslash
2+
3+// libvidya for the phone, from jolt-native's v0.1.0 release.
4+//
5+// arm64, API 28, built by NDK r29 through jolt-native's buck2 toolchain. The
6+// same object `just ffi-android` produces there, pinned so that building the
7+// APK needs neither that checkout nor an NDK.
8+//
9+// A sibling jolt-native checkout still wins over this: see the `buck` recipe
10+// in the justfile. Anyone working on both repos at once builds what they are
11+// editing; everyone else gets these bytes.
12+{
13+ "name": "libvidya-android",
14+ "platforms": {
15+ "linux-x86_64": {
16+ "size": 6721871,
17+ "hash": "sha256",
18+ "digest": "bb85c57ea263b9bfe113927c6f6b0c8c6414b97056bcb0281b8444187616a233",
19+ "format": "tar.gz",
20+ "path": "libvidya.so",
21+ "providers": [
22+ {
23+ "url": "https://gitlab.com/-/project/85910092/uploads/29c7b1aacb97cd0e1aea7f430a6fa225/jolt-native-android-arm64-v0.1.0.tar.gz"
24+ }
25+ ]
26+ },
27+ "linux-aarch64": {
28+ "size": 6721871,
29+ "hash": "sha256",
30+ "digest": "bb85c57ea263b9bfe113927c6f6b0c8c6414b97056bcb0281b8444187616a233",
31+ "format": "tar.gz",
32+ "path": "libvidya.so",
33+ "providers": [
34+ {
35+ "url": "https://gitlab.com/-/project/85910092/uploads/29c7b1aacb97cd0e1aea7f430a6fa225/jolt-native-android-arm64-v0.1.0.tar.gz"
36+ }
37+ ]
38+ },
39+ "macos-x86_64": {
40+ "size": 6721871,
41+ "hash": "sha256",
42+ "digest": "bb85c57ea263b9bfe113927c6f6b0c8c6414b97056bcb0281b8444187616a233",
43+ "format": "tar.gz",
44+ "path": "libvidya.so",
45+ "providers": [
46+ {
47+ "url": "https://gitlab.com/-/project/85910092/uploads/29c7b1aacb97cd0e1aea7f430a6fa225/jolt-native-android-arm64-v0.1.0.tar.gz"
48+ }
49+ ]
50+ },
51+ "macos-aarch64": {
52+ "size": 6721871,
53+ "hash": "sha256",
54+ "digest": "bb85c57ea263b9bfe113927c6f6b0c8c6414b97056bcb0281b8444187616a233",
55+ "format": "tar.gz",
56+ "path": "libvidya.so",
57+ "providers": [
58+ {
59+ "url": "https://gitlab.com/-/project/85910092/uploads/29c7b1aacb97cd0e1aea7f430a6fa225/jolt-native-android-arm64-v0.1.0.tar.gz"
60+ }
61+ ]
62+ }
63+ }
64+}
new file mode 100755
@@ -0,0 +1,64 @@
1+#!/usr/bin/env dotslash
2+
3+// libvidya for the phone, from jolt-native's v0.1.0 release.
4+//
5+// arm64, API 28, built by NDK r29 through jolt-native's buck2 toolchain. The
6+// same object `just ffi-android` produces there, pinned so that building the
7+// APK needs neither that checkout nor an NDK.
8+//
9+// A sibling jolt-native checkout still wins over this: see the `buck` recipe
10+// in the justfile. Anyone working on both repos at once builds what they are
11+// editing; everyone else gets these bytes.
12+{
13+ "name": "libvidya-android",
14+ "platforms": {
15+ "linux-x86_64": {
16+ "size": 6721871,
17+ "hash": "sha256",
18+ "digest": "bb85c57ea263b9bfe113927c6f6b0c8c6414b97056bcb0281b8444187616a233",
19+ "format": "tar.gz",
20+ "path": "libvidya.so",
21+ "providers": [
22+ {
23+ "url": "https://gitlab.com/-/project/85910092/uploads/29c7b1aacb97cd0e1aea7f430a6fa225/jolt-native-android-arm64-v0.1.0.tar.gz"
24+ }
25+ ]
26+ },
27+ "linux-aarch64": {
28+ "size": 6721871,
29+ "hash": "sha256",
30+ "digest": "bb85c57ea263b9bfe113927c6f6b0c8c6414b97056bcb0281b8444187616a233",
31+ "format": "tar.gz",
32+ "path": "libvidya.so",
33+ "providers": [
34+ {
35+ "url": "https://gitlab.com/-/project/85910092/uploads/29c7b1aacb97cd0e1aea7f430a6fa225/jolt-native-android-arm64-v0.1.0.tar.gz"
36+ }
37+ ]
38+ },
39+ "macos-x86_64": {
40+ "size": 6721871,
41+ "hash": "sha256",
42+ "digest": "bb85c57ea263b9bfe113927c6f6b0c8c6414b97056bcb0281b8444187616a233",
43+ "format": "tar.gz",
44+ "path": "libvidya.so",
45+ "providers": [
46+ {
47+ "url": "https://gitlab.com/-/project/85910092/uploads/29c7b1aacb97cd0e1aea7f430a6fa225/jolt-native-android-arm64-v0.1.0.tar.gz"
48+ }
49+ ]
50+ },
51+ "macos-aarch64": {
52+ "size": 6721871,
53+ "hash": "sha256",
54+ "digest": "bb85c57ea263b9bfe113927c6f6b0c8c6414b97056bcb0281b8444187616a233",
55+ "format": "tar.gz",
56+ "path": "libvidya.so",
57+ "providers": [
58+ {
59+ "url": "https://gitlab.com/-/project/85910092/uploads/29c7b1aacb97cd0e1aea7f430a6fa225/jolt-native-android-arm64-v0.1.0.tar.gz"
60+ }
61+ ]
62+ }
63+ }
64+}
added toolchains/BUCK +19 -0
new file mode 100644
@@ -0,0 +1,19 @@
1+# There is no compiler toolchain here, and that is the whole design.
2+#
3+# The APK's two native pieces are built by jolt-native: libvidya by its buck2
4+# graph, and the glue by the NDK its scripts/ pins. Everything this repo adds
5+# on top — the boot image, the dex, the packaging — is a tool invocation, so
6+# genrule is the only toolchain the graph needs. Nothing here is resolved
7+# against the machine except the Android SDK, which .buckconfig.local names.
8+load("@prelude//toolchains:genrule.bzl", "system_genrule_toolchain")
9+load("@prelude//toolchains:python.bzl", "system_python_bootstrap_toolchain")
10+
11+system_genrule_toolchain(
12+ name = "genrule",
13+ visibility = ["PUBLIC"],
14+)
15+
16+system_python_bootstrap_toolchain(
17+ name = "python_bootstrap",
18+ visibility = ["PUBLIC"],
19+)
new file mode 100644
@@ -0,0 +1,19 @@
1+# There is no compiler toolchain here, and that is the whole design.
2+#
3+# The APK's two native pieces are built by jolt-native: libvidya by its buck2
4+# graph, and the glue by the NDK its scripts/ pins. Everything this repo adds
5+# on top — the boot image, the dex, the packaging — is a tool invocation, so
6+# genrule is the only toolchain the graph needs. Nothing here is resolved
7+# against the machine except the Android SDK, which .buckconfig.local names.
8+load("@prelude//toolchains:genrule.bzl", "system_genrule_toolchain")
9+load("@prelude//toolchains:python.bzl", "system_python_bootstrap_toolchain")
10+
11+system_genrule_toolchain(
12+ name = "genrule",
13+ visibility = ["PUBLIC"],
14+)
15+
16+system_python_bootstrap_toolchain(
17+ name = "python_bootstrap",
18+ visibility = ["PUBLIC"],
19+)
added toolchains/dist.bzl +32 -0
new file mode 100644
@@ -0,0 +1,32 @@
1+# DotSlash, as a build artifact rather than something found on the machine.
2+#
3+# scripts/dotslash.dotslash is where the version and digest are written down,
4+# and it stays that way: dist/generated.bzl is derived from it by
5+# scripts/dotslash-to-buck, so there is one place to bump a version and no
6+# second copy of a digest to drift.
7+#
8+# Only DotSlash is fetched this way. jolt is not, and that is the point: what
9+# travels to a worker is the fetcher and the manifest, both small, and the
10+# worker resolves the manifest itself. See android/BUCK.
11+load("//dist:generated.bzl", "DIST")
12+
13+def dist_archive(name, tool, platform):
14+ """One pinned archive, fetched by buck rather than by a shim.
15+
16+ Unlike DotSlash, which resolves on the machine that runs a tool, this
17+ makes the archive's contents an input: their bytes are in the digest of
18+ every action that reads them, so a new release rebuilds what depends on
19+ it and an unchanged one rebuilds nothing.
20+ """
21+ entry = DIST[tool][platform]
22+ native.http_archive(
23+ name = name,
24+ urls = [entry["url"]],
25+ sha256 = entry["sha256"],
26+ strip_prefix = entry["strip_prefix"] or None,
27+ type = entry["type"],
28+ visibility = ["PUBLIC"],
29+ )
30+
31+def dotslash_dist(name, platform):
32+ dist_archive(name, "dotslash", platform)
new file mode 100644
@@ -0,0 +1,32 @@
1+# DotSlash, as a build artifact rather than something found on the machine.
2+#
3+# scripts/dotslash.dotslash is where the version and digest are written down,
4+# and it stays that way: dist/generated.bzl is derived from it by
5+# scripts/dotslash-to-buck, so there is one place to bump a version and no
6+# second copy of a digest to drift.
7+#
8+# Only DotSlash is fetched this way. jolt is not, and that is the point: what
9+# travels to a worker is the fetcher and the manifest, both small, and the
10+# worker resolves the manifest itself. See android/BUCK.
11+load("//dist:generated.bzl", "DIST")
12+
13+def dist_archive(name, tool, platform):
14+ """One pinned archive, fetched by buck rather than by a shim.
15+
16+ Unlike DotSlash, which resolves on the machine that runs a tool, this
17+ makes the archive's contents an input: their bytes are in the digest of
18+ every action that reads them, so a new release rebuilds what depends on
19+ it and an unchanged one rebuilds nothing.
20+ """
21+ entry = DIST[tool][platform]
22+ native.http_archive(
23+ name = name,
24+ urls = [entry["url"]],
25+ sha256 = entry["sha256"],
26+ strip_prefix = entry["strip_prefix"] or None,
27+ type = entry["type"],
28+ visibility = ["PUBLIC"],
29+ )
30+
31+def dotslash_dist(name, platform):
32+ dist_archive(name, "dotslash", platform)
added toolchains/dist/BUCK +13 -0
new file mode 100644
@@ -0,0 +1,13 @@
1+# The pinned fetcher. See ../dist.bzl; the version and digest come from
2+# scripts/dotslash.dotslash.
3+load("//:dist.bzl", "dist_archive", "dotslash_dist")
4+
5+dotslash_dist(name = "dotslash", platform = "linux-x86_64")
6+
7+# jolt-native's release. Both are what a build gets when there is no sibling
8+# checkout to prefer — see the `buck` recipe in the justfile.
9+dist_archive(name = "libvidya-android", tool = "libvidya-android", platform = "linux-x86_64")
10+
11+dist_archive(name = "glimmer-vidya", tool = "glimmer-vidya", platform = "linux-x86_64")
12+
13+dist_archive(name = "android-glue", tool = "android-glue", platform = "linux-x86_64")
new file mode 100644
@@ -0,0 +1,13 @@
1+# The pinned fetcher. See ../dist.bzl; the version and digest come from
2+# scripts/dotslash.dotslash.
3+load("//:dist.bzl", "dist_archive", "dotslash_dist")
4+
5+dotslash_dist(name = "dotslash", platform = "linux-x86_64")
6+
7+# jolt-native's release. Both are what a build gets when there is no sibling
8+# checkout to prefer — see the `buck` recipe in the justfile.
9+dist_archive(name = "libvidya-android", tool = "libvidya-android", platform = "linux-x86_64")
10+
11+dist_archive(name = "glimmer-vidya", tool = "glimmer-vidya", platform = "linux-x86_64")
12+
13+dist_archive(name = "android-glue", tool = "android-glue", platform = "linux-x86_64")
added toolchains/dist/generated.bzl +111 -0
new file mode 100644
@@ -0,0 +1,111 @@
1+# @generated by scripts/dotslash-to-buck — do not edit.
2+#
3+# The same archives scripts/*.dotslash pins, as facts buck can act on.
4+# Bump the DotSlash file and run `just sync-dist`.
5+
6+DIST = {
7+ "android-glue": {
8+ "linux-aarch64": {
9+ "url": "https://gitlab.com/-/project/85910092/uploads/16d2fe6e8df490940ca0a2a18ba4c092/jolt-native-android-glue-v0.1.0.tar.gz",
10+ "sha256": "8e9c5bae9c9919354430c9d3e888e6c3430d26520f9f429a7d2de2b850481133",
11+ "strip_prefix": "jolt-native-android-glue",
12+ "type": "tar.gz"
13+ },
14+ "linux-x86_64": {
15+ "url": "https://gitlab.com/-/project/85910092/uploads/16d2fe6e8df490940ca0a2a18ba4c092/jolt-native-android-glue-v0.1.0.tar.gz",
16+ "sha256": "8e9c5bae9c9919354430c9d3e888e6c3430d26520f9f429a7d2de2b850481133",
17+ "strip_prefix": "jolt-native-android-glue",
18+ "type": "tar.gz"
19+ },
20+ "macos-aarch64": {
21+ "url": "https://gitlab.com/-/project/85910092/uploads/16d2fe6e8df490940ca0a2a18ba4c092/jolt-native-android-glue-v0.1.0.tar.gz",
22+ "sha256": "8e9c5bae9c9919354430c9d3e888e6c3430d26520f9f429a7d2de2b850481133",
23+ "strip_prefix": "jolt-native-android-glue",
24+ "type": "tar.gz"
25+ },
26+ "macos-x86_64": {
27+ "url": "https://gitlab.com/-/project/85910092/uploads/16d2fe6e8df490940ca0a2a18ba4c092/jolt-native-android-glue-v0.1.0.tar.gz",
28+ "sha256": "8e9c5bae9c9919354430c9d3e888e6c3430d26520f9f429a7d2de2b850481133",
29+ "strip_prefix": "jolt-native-android-glue",
30+ "type": "tar.gz"
31+ }
32+ },
33+ "dotslash": {
34+ "linux-aarch64": {
35+ "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-linux-musl.arm64.v0.5.9.tar.gz",
36+ "sha256": "e58374446dacd4a228388cd275d974bbd8f946916b513cf0eb86bdff5d9d675e",
37+ "strip_prefix": "",
38+ "type": "tar.gz"
39+ },
40+ "linux-x86_64": {
41+ "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-linux-musl.x86_64.v0.5.9.tar.gz",
42+ "sha256": "4c75c6eb7890ae35993b962073f6d9bbe78b42b81a5691303ad70f63bfbf7196",
43+ "strip_prefix": "",
44+ "type": "tar.gz"
45+ },
46+ "macos-aarch64": {
47+ "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-macos-arm64.v0.5.9.tar.gz",
48+ "sha256": "eef15be1b554de7b1409cb0c69c24f2cd04f19c0f101ad69fcdf9d66d4d51639",
49+ "strip_prefix": "",
50+ "type": "tar.gz"
51+ },
52+ "macos-x86_64": {
53+ "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-macos-amd64.v0.5.9.tar.gz",
54+ "sha256": "51446593596ffe4cd22bc43a0fe6d82dd367758eb53cb4ade36d6a684ed08469",
55+ "strip_prefix": "",
56+ "type": "tar.gz"
57+ }
58+ },
59+ "glimmer-vidya": {
60+ "linux-aarch64": {
61+ "url": "https://gitlab.com/-/project/85910092/uploads/3448c1fde6a113a5578335ac54811d0f/jolt-native-glimmer-vidya-v0.1.0.tar.gz",
62+ "sha256": "3842a5981c0656eb14915aead80814286040c24e83732d5e69d4f4470308fbf5",
63+ "strip_prefix": "glimmer-vidya",
64+ "type": "tar.gz"
65+ },
66+ "linux-x86_64": {
67+ "url": "https://gitlab.com/-/project/85910092/uploads/3448c1fde6a113a5578335ac54811d0f/jolt-native-glimmer-vidya-v0.1.0.tar.gz",
68+ "sha256": "3842a5981c0656eb14915aead80814286040c24e83732d5e69d4f4470308fbf5",
69+ "strip_prefix": "glimmer-vidya",
70+ "type": "tar.gz"
71+ },
72+ "macos-aarch64": {
73+ "url": "https://gitlab.com/-/project/85910092/uploads/3448c1fde6a113a5578335ac54811d0f/jolt-native-glimmer-vidya-v0.1.0.tar.gz",
74+ "sha256": "3842a5981c0656eb14915aead80814286040c24e83732d5e69d4f4470308fbf5",
75+ "strip_prefix": "glimmer-vidya",
76+ "type": "tar.gz"
77+ },
78+ "macos-x86_64": {
79+ "url": "https://gitlab.com/-/project/85910092/uploads/3448c1fde6a113a5578335ac54811d0f/jolt-native-glimmer-vidya-v0.1.0.tar.gz",
80+ "sha256": "3842a5981c0656eb14915aead80814286040c24e83732d5e69d4f4470308fbf5",
81+ "strip_prefix": "glimmer-vidya",
82+ "type": "tar.gz"
83+ }
84+ },
85+ "libvidya-android": {
86+ "linux-aarch64": {
87+ "url": "https://gitlab.com/-/project/85910092/uploads/29c7b1aacb97cd0e1aea7f430a6fa225/jolt-native-android-arm64-v0.1.0.tar.gz",
88+ "sha256": "bb85c57ea263b9bfe113927c6f6b0c8c6414b97056bcb0281b8444187616a233",
89+ "strip_prefix": "",
90+ "type": "tar.gz"
91+ },
92+ "linux-x86_64": {
93+ "url": "https://gitlab.com/-/project/85910092/uploads/29c7b1aacb97cd0e1aea7f430a6fa225/jolt-native-android-arm64-v0.1.0.tar.gz",
94+ "sha256": "bb85c57ea263b9bfe113927c6f6b0c8c6414b97056bcb0281b8444187616a233",
95+ "strip_prefix": "",
96+ "type": "tar.gz"
97+ },
98+ "macos-aarch64": {
99+ "url": "https://gitlab.com/-/project/85910092/uploads/29c7b1aacb97cd0e1aea7f430a6fa225/jolt-native-android-arm64-v0.1.0.tar.gz",
100+ "sha256": "bb85c57ea263b9bfe113927c6f6b0c8c6414b97056bcb0281b8444187616a233",
101+ "strip_prefix": "",
102+ "type": "tar.gz"
103+ },
104+ "macos-x86_64": {
105+ "url": "https://gitlab.com/-/project/85910092/uploads/29c7b1aacb97cd0e1aea7f430a6fa225/jolt-native-android-arm64-v0.1.0.tar.gz",
106+ "sha256": "bb85c57ea263b9bfe113927c6f6b0c8c6414b97056bcb0281b8444187616a233",
107+ "strip_prefix": "",
108+ "type": "tar.gz"
109+ }
110+ }
111+}
new file mode 100644
@@ -0,0 +1,111 @@
1+# @generated by scripts/dotslash-to-buck — do not edit.
2+#
3+# The same archives scripts/*.dotslash pins, as facts buck can act on.
4+# Bump the DotSlash file and run `just sync-dist`.
5+
6+DIST = {
7+ "android-glue": {
8+ "linux-aarch64": {
9+ "url": "https://gitlab.com/-/project/85910092/uploads/16d2fe6e8df490940ca0a2a18ba4c092/jolt-native-android-glue-v0.1.0.tar.gz",
10+ "sha256": "8e9c5bae9c9919354430c9d3e888e6c3430d26520f9f429a7d2de2b850481133",
11+ "strip_prefix": "jolt-native-android-glue",
12+ "type": "tar.gz"
13+ },
14+ "linux-x86_64": {
15+ "url": "https://gitlab.com/-/project/85910092/uploads/16d2fe6e8df490940ca0a2a18ba4c092/jolt-native-android-glue-v0.1.0.tar.gz",
16+ "sha256": "8e9c5bae9c9919354430c9d3e888e6c3430d26520f9f429a7d2de2b850481133",
17+ "strip_prefix": "jolt-native-android-glue",
18+ "type": "tar.gz"
19+ },
20+ "macos-aarch64": {
21+ "url": "https://gitlab.com/-/project/85910092/uploads/16d2fe6e8df490940ca0a2a18ba4c092/jolt-native-android-glue-v0.1.0.tar.gz",
22+ "sha256": "8e9c5bae9c9919354430c9d3e888e6c3430d26520f9f429a7d2de2b850481133",
23+ "strip_prefix": "jolt-native-android-glue",
24+ "type": "tar.gz"
25+ },
26+ "macos-x86_64": {
27+ "url": "https://gitlab.com/-/project/85910092/uploads/16d2fe6e8df490940ca0a2a18ba4c092/jolt-native-android-glue-v0.1.0.tar.gz",
28+ "sha256": "8e9c5bae9c9919354430c9d3e888e6c3430d26520f9f429a7d2de2b850481133",
29+ "strip_prefix": "jolt-native-android-glue",
30+ "type": "tar.gz"
31+ }
32+ },
33+ "dotslash": {
34+ "linux-aarch64": {
35+ "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-linux-musl.arm64.v0.5.9.tar.gz",
36+ "sha256": "e58374446dacd4a228388cd275d974bbd8f946916b513cf0eb86bdff5d9d675e",
37+ "strip_prefix": "",
38+ "type": "tar.gz"
39+ },
40+ "linux-x86_64": {
41+ "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-linux-musl.x86_64.v0.5.9.tar.gz",
42+ "sha256": "4c75c6eb7890ae35993b962073f6d9bbe78b42b81a5691303ad70f63bfbf7196",
43+ "strip_prefix": "",
44+ "type": "tar.gz"
45+ },
46+ "macos-aarch64": {
47+ "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-macos-arm64.v0.5.9.tar.gz",
48+ "sha256": "eef15be1b554de7b1409cb0c69c24f2cd04f19c0f101ad69fcdf9d66d4d51639",
49+ "strip_prefix": "",
50+ "type": "tar.gz"
51+ },
52+ "macos-x86_64": {
53+ "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-macos-amd64.v0.5.9.tar.gz",
54+ "sha256": "51446593596ffe4cd22bc43a0fe6d82dd367758eb53cb4ade36d6a684ed08469",
55+ "strip_prefix": "",
56+ "type": "tar.gz"
57+ }
58+ },
59+ "glimmer-vidya": {
60+ "linux-aarch64": {
61+ "url": "https://gitlab.com/-/project/85910092/uploads/3448c1fde6a113a5578335ac54811d0f/jolt-native-glimmer-vidya-v0.1.0.tar.gz",
62+ "sha256": "3842a5981c0656eb14915aead80814286040c24e83732d5e69d4f4470308fbf5",
63+ "strip_prefix": "glimmer-vidya",
64+ "type": "tar.gz"
65+ },
66+ "linux-x86_64": {
67+ "url": "https://gitlab.com/-/project/85910092/uploads/3448c1fde6a113a5578335ac54811d0f/jolt-native-glimmer-vidya-v0.1.0.tar.gz",
68+ "sha256": "3842a5981c0656eb14915aead80814286040c24e83732d5e69d4f4470308fbf5",
69+ "strip_prefix": "glimmer-vidya",
70+ "type": "tar.gz"
71+ },
72+ "macos-aarch64": {
73+ "url": "https://gitlab.com/-/project/85910092/uploads/3448c1fde6a113a5578335ac54811d0f/jolt-native-glimmer-vidya-v0.1.0.tar.gz",
74+ "sha256": "3842a5981c0656eb14915aead80814286040c24e83732d5e69d4f4470308fbf5",
75+ "strip_prefix": "glimmer-vidya",
76+ "type": "tar.gz"
77+ },
78+ "macos-x86_64": {
79+ "url": "https://gitlab.com/-/project/85910092/uploads/3448c1fde6a113a5578335ac54811d0f/jolt-native-glimmer-vidya-v0.1.0.tar.gz",
80+ "sha256": "3842a5981c0656eb14915aead80814286040c24e83732d5e69d4f4470308fbf5",
81+ "strip_prefix": "glimmer-vidya",
82+ "type": "tar.gz"
83+ }
84+ },
85+ "libvidya-android": {
86+ "linux-aarch64": {
87+ "url": "https://gitlab.com/-/project/85910092/uploads/29c7b1aacb97cd0e1aea7f430a6fa225/jolt-native-android-arm64-v0.1.0.tar.gz",
88+ "sha256": "bb85c57ea263b9bfe113927c6f6b0c8c6414b97056bcb0281b8444187616a233",
89+ "strip_prefix": "",
90+ "type": "tar.gz"
91+ },
92+ "linux-x86_64": {
93+ "url": "https://gitlab.com/-/project/85910092/uploads/29c7b1aacb97cd0e1aea7f430a6fa225/jolt-native-android-arm64-v0.1.0.tar.gz",
94+ "sha256": "bb85c57ea263b9bfe113927c6f6b0c8c6414b97056bcb0281b8444187616a233",
95+ "strip_prefix": "",
96+ "type": "tar.gz"
97+ },
98+ "macos-aarch64": {
99+ "url": "https://gitlab.com/-/project/85910092/uploads/29c7b1aacb97cd0e1aea7f430a6fa225/jolt-native-android-arm64-v0.1.0.tar.gz",
100+ "sha256": "bb85c57ea263b9bfe113927c6f6b0c8c6414b97056bcb0281b8444187616a233",
101+ "strip_prefix": "",
102+ "type": "tar.gz"
103+ },
104+ "macos-x86_64": {
105+ "url": "https://gitlab.com/-/project/85910092/uploads/29c7b1aacb97cd0e1aea7f430a6fa225/jolt-native-android-arm64-v0.1.0.tar.gz",
106+ "sha256": "bb85c57ea263b9bfe113927c6f6b0c8c6414b97056bcb0281b8444187616a233",
107+ "strip_prefix": "",
108+ "type": "tar.gz"
109+ }
110+ }
111+}