Cross-compile for Android, with an NDK nobody has to install
zig cc carries a glibc sysroot and no bionic, so there is nothing on this side to link an Android object against. The NDK's clang is the driver that knows where libc.so and the platform stubs are, and this puts one behind the same hermetic_cxx_toolchain zig already sits behind: only the archive and the wrapper change, because the toolchain asks a wrapper for a mode rather than naming binaries itself. The archive is the one thing here buck does not carry. Unpacked the NDK is three gigabytes, and an input that size reaches a remote worker only by being uploaded to the CAS first — 276MiB on the wire before a single action runs. What travels instead is DotSlash and the manifest naming the archive, both small, and whatever machine runs the action fetches the same bytes under the same digest from Google. A worker with a warm cache does nothing at all. The cost is stated in ndk.sh: what the compiler *is* stops being part of the action's digest. The manifest is an input, so a version bump still invalidates, and DotSlash keys its cache on the digest, so a machine cannot be holding something else under that name. The Rust half needed the device's libstd, which no single dist tarball carries: rustc resolves a target's std from sysroot/lib/rustlib/<triple> and accepts --sysroot once, so cross-compiling wants two triples in one directory. scripts/rustc has always stitched that by hand into a cache under $HOME; toolchains//dist:android-sysroot does it as an artifact instead, which is a thing an action can depend on and a worker can be handed. selftest/ is what keeps both honest — one C object and one Rust one that compile only for the device, and fail loudly if the sysroot, the API level or the libstd is not what it should be. Nothing ships them.
14a30c0 parent: 5a37b4b modified
platforms/BUCK +13 -0 | @@ -28,3 +28,16 @@ execution_platform( | ||
| 28 | 28 | use_windows_path_separators = host_info().os.is_windows, |
| 29 | 29 | visibility = ["PUBLIC"], |
| 30 | 30 | ) |
| 31 | + | |
| 32 | +# A *target* platform, unlike the two above: this is what a graph is built | |
| 33 | +# for, not where the building runs. Naming it on a configured_alias moves the | |
| 34 | +# target configuration and leaves the execution platform alone, so build | |
| 35 | +# scripts and proc macros still compile and run on the host. | |
| 36 | +platform( | |
| 37 | + name = "android-arm64", | |
| 38 | + constraint_values = [ | |
| 39 | + "prelude//os/constraints:os[android]", | |
| 40 | + "prelude//cpu/constraints:cpu[arm64]", | |
| 41 | + ], | |
| 42 | + visibility = ["PUBLIC"], | |
| 43 | +) | |
| @@ -28,3 +28,16 @@ execution_platform( | |||
| 28 | use_windows_path_separators = host_info().os.is_windows, | 28 | use_windows_path_separators = host_info().os.is_windows, |
| 29 | visibility = ["PUBLIC"], | 29 | visibility = ["PUBLIC"], |
| 30 | ) | 30 | ) |
| 31 | + | ||
| 32 | +# A *target* platform, unlike the two above: this is what a graph is built | ||
| 33 | +# for, not where the building runs. Naming it on a configured_alias moves the | ||
| 34 | +# target configuration and leaves the execution platform alone, so build | ||
| 35 | +# scripts and proc macros still compile and run on the host. | ||
| 36 | +platform( | ||
| 37 | + name = "android-arm64", | ||
| 38 | + constraint_values = [ | ||
| 39 | + "prelude//os/constraints:os[android]", | ||
| 40 | + "prelude//cpu/constraints:cpu[arm64]", | ||
| 41 | + ], | ||
| 42 | + visibility = ["PUBLIC"], | ||
| 43 | +) | ||
added
scripts/BUCK +11 -0 | new file mode 100644 | ||
| @@ -0,0 +1,11 @@ | ||
| 1 | +# The pins, as artifacts. | |
| 2 | +# | |
| 3 | +# A manifest that a *build action* resolves has to be an input to it, so that | |
| 4 | +# bumping the version it names invalidates what was built with the old one. | |
| 5 | +# See toolchains/ndk.sh, which is handed this file and a DotSlash to read it | |
| 6 | +# with. | |
| 7 | +export_file( | |
| 8 | + name = "android-ndk.dotslash", | |
| 9 | + mode = "reference", | |
| 10 | + visibility = ["PUBLIC"], | |
| 11 | +) | |
| new file mode 100644 | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | +# The pins, as artifacts. | ||
| 2 | +# | ||
| 3 | +# A manifest that a *build action* resolves has to be an input to it, so that | ||
| 4 | +# bumping the version it names invalidates what was built with the old one. | ||
| 5 | +# See toolchains/ndk.sh, which is handed this file and a DotSlash to read it | ||
| 6 | +# with. | ||
| 7 | +export_file( | ||
| 8 | + name = "android-ndk.dotslash", | ||
| 9 | + mode = "reference", | ||
| 10 | + visibility = ["PUBLIC"], | ||
| 11 | +) | ||
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-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 | +} | ||
modified
scripts/dotslash-to-buck +16 -2 | @@ -24,7 +24,17 @@ OUT = HERE.parent / "toolchains" / "dist" / "generated.bzl" | ||
| 24 | 24 | |
| 25 | 25 | # The tools buck fetches for itself. buck2 is not among them: it is the thing |
| 26 | 26 | # doing the fetching, and dotslash is how a human gets it. |
| 27 | -TOOLS = {"zig": "zig", "rustc": "rust"} | |
| 27 | +TOOLS = { | |
| 28 | + "zig": "zig", | |
| 29 | + "rustc": "rust", | |
| 30 | + "android-ndk": "android-ndk", | |
| 31 | + # Target-only: the same archive whatever the host, which is why | |
| 32 | + # every platform in that manifest carries the same digest. | |
| 33 | + "rust-std-android": "rust-std-android", | |
| 34 | + # Fetched so it can do the fetching, on a machine this one cannot upload | |
| 35 | + # three gigabytes to. See the manifest. | |
| 36 | + "dotslash": "dotslash", | |
| 37 | +} | |
| 28 | 38 | |
| 29 | 39 | |
| 30 | 40 | def load(path): |
| @@ -50,10 +60,14 @@ def main(): | ||
| 50 | 60 | url = entry["providers"][0]["url"] |
| 51 | 61 | # The path of the binary inside the archive starts with the |
| 52 | 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("/") | |
| 53 | 67 | platforms[platform] = { |
| 54 | 68 | "url": url, |
| 55 | 69 | "sha256": entry["digest"], |
| 56 | - "strip_prefix": entry["path"].split("/")[0], | |
| 70 | + "strip_prefix": head if rest else "", | |
| 57 | 71 | "type": entry["format"], |
| 58 | 72 | } |
| 59 | 73 | entries[name] = platforms |
| @@ -24,7 +24,17 @@ OUT = HERE.parent / "toolchains" / "dist" / "generated.bzl" | |||
| 24 | 24 | ||
| 25 | # The tools buck fetches for itself. buck2 is not among them: it is the thing | 25 | # The tools buck fetches for itself. buck2 is not among them: it is the thing |
| 26 | # doing the fetching, and dotslash is how a human gets it. | 26 | # doing the fetching, and dotslash is how a human gets it. |
| 27 | -TOOLS = {"zig": "zig", "rustc": "rust"} | 27 | +TOOLS = { |
| 28 | + "zig": "zig", | ||
| 29 | + "rustc": "rust", | ||
| 30 | + "android-ndk": "android-ndk", | ||
| 31 | + # Target-only: the same archive whatever the host, which is why | ||
| 32 | + # every platform in that manifest carries the same digest. | ||
| 33 | + "rust-std-android": "rust-std-android", | ||
| 34 | + # Fetched so it can do the fetching, on a machine this one cannot upload | ||
| 35 | + # three gigabytes to. See the manifest. | ||
| 36 | + "dotslash": "dotslash", | ||
| 37 | +} | ||
| 28 | 38 | ||
| 29 | 39 | ||
| 30 | def load(path): | 40 | def load(path): |
| @@ -50,10 +60,14 @@ def main(): | |||
| 50 | url = entry["providers"][0]["url"] | 60 | url = entry["providers"][0]["url"] |
| 51 | # The path of the binary inside the archive starts with the | 61 | # The path of the binary inside the archive starts with the |
| 52 | # directory the tarball unpacks into, which is what to strip. | 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("/") | ||
| 53 | platforms[platform] = { | 67 | platforms[platform] = { |
| 54 | "url": url, | 68 | "url": url, |
| 55 | "sha256": entry["digest"], | 69 | "sha256": entry["digest"], |
| 56 | - "strip_prefix": entry["path"].split("/")[0], | 70 | + "strip_prefix": head if rest else "", |
| 57 | "type": entry["format"], | 71 | "type": entry["format"], |
| 58 | } | 72 | } |
| 59 | entries[name] = platforms | 73 | entries[name] = platforms |
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 | +} | ||
modified
toolchains/BUCK +74 -11 | @@ -1,3 +1,4 @@ | ||
| 1 | +load("//dist:generated.bzl", "DIST") | |
| 1 | 2 | load(":defs.bzl", "hermetic_cxx_toolchain", "hermetic_rust_toolchain") |
| 2 | 3 | load("@prelude//toolchains:genrule.bzl", "system_genrule_toolchain") |
| 3 | 4 | load("@prelude//toolchains:python.bzl", "system_python_bootstrap_toolchain") |
| @@ -34,16 +35,45 @@ _SCRIPTS = read_root_config("jolt", "scripts", "scripts") | ||
| 34 | 35 | # by the old one — the E0514 failure, one toolchain over. Naming the version in |
| 35 | 36 | # a cfg nothing reads keeps the digests apart until zig is a dependency too. |
| 36 | 37 | _ZIG_VERSION = read_root_config("jolt", "zig_version", "unknown") |
| 38 | + | |
| 39 | +# The NDK's, for the same reason and read from the pin rather than the machine. | |
| 40 | +# The C toolchain does not need it — the archive is a dependency there, so it | |
| 41 | +# is in every cxx action's digest already. rustc does: it links an Android | |
| 42 | +# target through scripts/android-cc, an absolute path that does not change when | |
| 43 | +# the NDK behind it does. | |
| 44 | +_NDK_VERSION = DIST["android-ndk"]["linux-x86_64"]["strip_prefix"] | |
| 37 | 45 | # |
| 38 | 46 | # This is the same arrangement .cargo/config.toml used to describe: zig cc |
| 39 | 47 | # carries its own glibc sysroot, which is why this repo builds on a machine |
| 40 | -# with no `cc` at all. Unlike vidya there is no Android row here — both shared | |
| 41 | -# objects this repo ships are desktop, and the APK takes its libvidya from | |
| 42 | -# vidya's own buck2 build. | |
| 48 | +# with no `cc` at all. | |
| 49 | +# | |
| 50 | +# Android is the one target it cannot serve. zig brings a glibc sysroot and no | |
| 51 | +# bionic, so there is nothing there to link an Android object against; the | |
| 52 | +# NDK's own clang is the driver that knows where libc.so and the platform stubs | |
| 53 | +# live. Only the archive and the wrapper change — the toolchain either side of | |
| 54 | +# them is the same construction, which is the point of asking a wrapper for a | |
| 55 | +# mode rather than naming binaries here. | |
| 43 | 56 | hermetic_cxx_toolchain( |
| 44 | 57 | name = "cxx", |
| 45 | - dist = "toolchains//dist:zig", | |
| 46 | - wrapper = "toolchains//:zcc.sh", | |
| 58 | + dist = select({ | |
| 59 | + "DEFAULT": "toolchains//dist:zig", | |
| 60 | + # DotSlash, not the NDK: ndk.sh fetches the NDK where it runs, which is | |
| 61 | + # the difference between a worker downloading three gigabytes from | |
| 62 | + # Google and this machine uploading them to the CAS first. | |
| 63 | + "prelude//os:android": "toolchains//dist:dotslash", | |
| 64 | + }), | |
| 65 | + manifest = select({ | |
| 66 | + "DEFAULT": None, | |
| 67 | + "prelude//os:android": "root//scripts:android-ndk.dotslash", | |
| 68 | + }), | |
| 69 | + wrapper = select({ | |
| 70 | + "DEFAULT": "toolchains//:zcc.sh", | |
| 71 | + "prelude//os:android": "toolchains//:ndk.sh", | |
| 72 | + }), | |
| 73 | + platform_name = select({ | |
| 74 | + "DEFAULT": "x86_64", | |
| 75 | + "prelude//os:android": "arm64", | |
| 76 | + }), | |
| 47 | 77 | visibility = ["PUBLIC"], |
| 48 | 78 | ) |
| 49 | 79 | |
| @@ -65,8 +95,32 @@ hermetic_rust_toolchain( | ||
| 65 | 95 | dist = "toolchains//dist:rust", |
| 66 | 96 | wrapper = "toolchains//:rustc.sh", |
| 67 | 97 | default_edition = "2021", |
| 68 | - rustc_flags = [ | |
| 69 | - "-Clinker=" + _SCRIPTS + "/zcc", | |
| 98 | + # Pipelined builds compile each crate against its dependencies' | |
| 99 | + # `-Zno-codegen` metadata rather than their rlibs. On the Android graph the | |
| 100 | + # two disagree for android-activity, and what comes out the far side is a | |
| 101 | + # link that cannot find winit — or glutin-winit, or anything else that | |
| 102 | + # reaches the event loop through it. Build real rlibs there. The desktop | |
| 103 | + # graph pipelines fine and keeps the parallelism. | |
| 104 | + nightly_features = select({ | |
| 105 | + "DEFAULT": True, | |
| 106 | + "prelude//os:android": False, | |
| 107 | + }), | |
| 108 | + # Which libstd rustc is given. The desktop configuration takes the | |
| 109 | + # component the dist tarball carries for its own host; Android needs two | |
| 110 | + # triples in one directory, so it takes the stitched one. | |
| 111 | + sysroot = select({ | |
| 112 | + "DEFAULT": None, | |
| 113 | + "prelude//os:android": "toolchains//dist:android-sysroot", | |
| 114 | + }), | |
| 115 | + rustc_flags = select({ | |
| 116 | + "DEFAULT": ["-Clinker=" + _SCRIPTS + "/zcc"], | |
| 117 | + # zig has no bionic to link against; the NDK's clang does. The cfg | |
| 118 | + # nothing reads is what keeps the digests apart, as above. | |
| 119 | + "prelude//os:android": [ | |
| 120 | + "-Clinker=" + _SCRIPTS + "/android-cc", | |
| 121 | + "--cfg=jolt_ndk=\"" + _NDK_VERSION + "\"", | |
| 122 | + ], | |
| 123 | + }) + [ | |
| 70 | 124 | # What [profile.release] in Cargo.toml says, and for the reason given |
| 71 | 125 | # there: the tree walk and the texture uploads are hot every frame, and |
| 72 | 126 | # the media plane decodes H.264. buck2 optimises nothing by default, so |
| @@ -75,10 +129,13 @@ hermetic_rust_toolchain( | ||
| 75 | 129 | "-Copt-level=2", |
| 76 | 130 | "--cfg=jolt_cc=\"" + _ZIG_VERSION + "\"", |
| 77 | 131 | ], |
| 78 | - # A plain string rather than a select: the sysroot path inside the dist | |
| 79 | - # tarball is built from this, and only the triples dist.bzl fetches an | |
| 80 | - # archive for can be named here. | |
| 81 | - rustc_target_triple = "x86_64-unknown-linux-gnu", | |
| 132 | + # Only triples there is an archive for can be named here: the desktop one | |
| 133 | + # comes out of the dist tarball, the Android one out of the libstd fetched | |
| 134 | + # beside it. | |
| 135 | + rustc_target_triple = select({ | |
| 136 | + "DEFAULT": "x86_64-unknown-linux-gnu", | |
| 137 | + "prelude//os:android": "aarch64-linux-android", | |
| 138 | + }), | |
| 82 | 139 | visibility = ["PUBLIC"], |
| 83 | 140 | ) |
| 84 | 141 | |
| @@ -90,6 +147,12 @@ export_file( | ||
| 90 | 147 | visibility = ["PUBLIC"], |
| 91 | 148 | ) |
| 92 | 149 | |
| 150 | +export_file( | |
| 151 | + name = "ndk.sh", | |
| 152 | + mode = "reference", | |
| 153 | + visibility = ["PUBLIC"], | |
| 154 | +) | |
| 155 | + | |
| 93 | 156 | export_file( |
| 94 | 157 | name = "rustc.sh", |
| 95 | 158 | mode = "reference", |
| @@ -1,3 +1,4 @@ | |||
| 1 | +load("//dist:generated.bzl", "DIST") | ||
| 1 | load(":defs.bzl", "hermetic_cxx_toolchain", "hermetic_rust_toolchain") | 2 | load(":defs.bzl", "hermetic_cxx_toolchain", "hermetic_rust_toolchain") |
| 2 | load("@prelude//toolchains:genrule.bzl", "system_genrule_toolchain") | 3 | load("@prelude//toolchains:genrule.bzl", "system_genrule_toolchain") |
| 3 | load("@prelude//toolchains:python.bzl", "system_python_bootstrap_toolchain") | 4 | load("@prelude//toolchains:python.bzl", "system_python_bootstrap_toolchain") |
| @@ -34,16 +35,45 @@ _SCRIPTS = read_root_config("jolt", "scripts", "scripts") | |||
| 34 | # by the old one — the E0514 failure, one toolchain over. Naming the version in | 35 | # by the old one — the E0514 failure, one toolchain over. Naming the version in |
| 35 | # a cfg nothing reads keeps the digests apart until zig is a dependency too. | 36 | # a cfg nothing reads keeps the digests apart until zig is a dependency too. |
| 36 | _ZIG_VERSION = read_root_config("jolt", "zig_version", "unknown") | 37 | _ZIG_VERSION = read_root_config("jolt", "zig_version", "unknown") |
| 38 | + | ||
| 39 | +# The NDK's, for the same reason and read from the pin rather than the machine. | ||
| 40 | +# The C toolchain does not need it — the archive is a dependency there, so it | ||
| 41 | +# is in every cxx action's digest already. rustc does: it links an Android | ||
| 42 | +# target through scripts/android-cc, an absolute path that does not change when | ||
| 43 | +# the NDK behind it does. | ||
| 44 | +_NDK_VERSION = DIST["android-ndk"]["linux-x86_64"]["strip_prefix"] | ||
| 37 | # | 45 | # |
| 38 | # This is the same arrangement .cargo/config.toml used to describe: zig cc | 46 | # This is the same arrangement .cargo/config.toml used to describe: zig cc |
| 39 | # carries its own glibc sysroot, which is why this repo builds on a machine | 47 | # carries its own glibc sysroot, which is why this repo builds on a machine |
| 40 | -# with no `cc` at all. Unlike vidya there is no Android row here — both shared | 48 | +# with no `cc` at all. |
| 41 | -# objects this repo ships are desktop, and the APK takes its libvidya from | 49 | +# |
| 42 | -# vidya's own buck2 build. | 50 | +# Android is the one target it cannot serve. zig brings a glibc sysroot and no |
| 51 | +# bionic, so there is nothing there to link an Android object against; the | ||
| 52 | +# NDK's own clang is the driver that knows where libc.so and the platform stubs | ||
| 53 | +# live. Only the archive and the wrapper change — the toolchain either side of | ||
| 54 | +# them is the same construction, which is the point of asking a wrapper for a | ||
| 55 | +# mode rather than naming binaries here. | ||
| 43 | hermetic_cxx_toolchain( | 56 | hermetic_cxx_toolchain( |
| 44 | name = "cxx", | 57 | name = "cxx", |
| 45 | - dist = "toolchains//dist:zig", | 58 | + dist = select({ |
| 46 | - wrapper = "toolchains//:zcc.sh", | 59 | + "DEFAULT": "toolchains//dist:zig", |
| 60 | + # DotSlash, not the NDK: ndk.sh fetches the NDK where it runs, which is | ||
| 61 | + # the difference between a worker downloading three gigabytes from | ||
| 62 | + # Google and this machine uploading them to the CAS first. | ||
| 63 | + "prelude//os:android": "toolchains//dist:dotslash", | ||
| 64 | + }), | ||
| 65 | + manifest = select({ | ||
| 66 | + "DEFAULT": None, | ||
| 67 | + "prelude//os:android": "root//scripts:android-ndk.dotslash", | ||
| 68 | + }), | ||
| 69 | + wrapper = select({ | ||
| 70 | + "DEFAULT": "toolchains//:zcc.sh", | ||
| 71 | + "prelude//os:android": "toolchains//:ndk.sh", | ||
| 72 | + }), | ||
| 73 | + platform_name = select({ | ||
| 74 | + "DEFAULT": "x86_64", | ||
| 75 | + "prelude//os:android": "arm64", | ||
| 76 | + }), | ||
| 47 | visibility = ["PUBLIC"], | 77 | visibility = ["PUBLIC"], |
| 48 | ) | 78 | ) |
| 49 | 79 | ||
| @@ -65,8 +95,32 @@ hermetic_rust_toolchain( | |||
| 65 | dist = "toolchains//dist:rust", | 95 | dist = "toolchains//dist:rust", |
| 66 | wrapper = "toolchains//:rustc.sh", | 96 | wrapper = "toolchains//:rustc.sh", |
| 67 | default_edition = "2021", | 97 | default_edition = "2021", |
| 68 | - rustc_flags = [ | 98 | + # Pipelined builds compile each crate against its dependencies' |
| 69 | - "-Clinker=" + _SCRIPTS + "/zcc", | 99 | + # `-Zno-codegen` metadata rather than their rlibs. On the Android graph the |
| 100 | + # two disagree for android-activity, and what comes out the far side is a | ||
| 101 | + # link that cannot find winit — or glutin-winit, or anything else that | ||
| 102 | + # reaches the event loop through it. Build real rlibs there. The desktop | ||
| 103 | + # graph pipelines fine and keeps the parallelism. | ||
| 104 | + nightly_features = select({ | ||
| 105 | + "DEFAULT": True, | ||
| 106 | + "prelude//os:android": False, | ||
| 107 | + }), | ||
| 108 | + # Which libstd rustc is given. The desktop configuration takes the | ||
| 109 | + # component the dist tarball carries for its own host; Android needs two | ||
| 110 | + # triples in one directory, so it takes the stitched one. | ||
| 111 | + sysroot = select({ | ||
| 112 | + "DEFAULT": None, | ||
| 113 | + "prelude//os:android": "toolchains//dist:android-sysroot", | ||
| 114 | + }), | ||
| 115 | + rustc_flags = select({ | ||
| 116 | + "DEFAULT": ["-Clinker=" + _SCRIPTS + "/zcc"], | ||
| 117 | + # zig has no bionic to link against; the NDK's clang does. The cfg | ||
| 118 | + # nothing reads is what keeps the digests apart, as above. | ||
| 119 | + "prelude//os:android": [ | ||
| 120 | + "-Clinker=" + _SCRIPTS + "/android-cc", | ||
| 121 | + "--cfg=jolt_ndk=\"" + _NDK_VERSION + "\"", | ||
| 122 | + ], | ||
| 123 | + }) + [ | ||
| 70 | # What [profile.release] in Cargo.toml says, and for the reason given | 124 | # What [profile.release] in Cargo.toml says, and for the reason given |
| 71 | # there: the tree walk and the texture uploads are hot every frame, and | 125 | # there: the tree walk and the texture uploads are hot every frame, and |
| 72 | # the media plane decodes H.264. buck2 optimises nothing by default, so | 126 | # the media plane decodes H.264. buck2 optimises nothing by default, so |
| @@ -75,10 +129,13 @@ hermetic_rust_toolchain( | |||
| 75 | "-Copt-level=2", | 129 | "-Copt-level=2", |
| 76 | "--cfg=jolt_cc=\"" + _ZIG_VERSION + "\"", | 130 | "--cfg=jolt_cc=\"" + _ZIG_VERSION + "\"", |
| 77 | ], | 131 | ], |
| 78 | - # A plain string rather than a select: the sysroot path inside the dist | 132 | + # Only triples there is an archive for can be named here: the desktop one |
| 79 | - # tarball is built from this, and only the triples dist.bzl fetches an | 133 | + # comes out of the dist tarball, the Android one out of the libstd fetched |
| 80 | - # archive for can be named here. | 134 | + # beside it. |
| 81 | - rustc_target_triple = "x86_64-unknown-linux-gnu", | 135 | + rustc_target_triple = select({ |
| 136 | + "DEFAULT": "x86_64-unknown-linux-gnu", | ||
| 137 | + "prelude//os:android": "aarch64-linux-android", | ||
| 138 | + }), | ||
| 82 | visibility = ["PUBLIC"], | 139 | visibility = ["PUBLIC"], |
| 83 | ) | 140 | ) |
| 84 | 141 | ||
| @@ -90,6 +147,12 @@ export_file( | |||
| 90 | visibility = ["PUBLIC"], | 147 | visibility = ["PUBLIC"], |
| 91 | ) | 148 | ) |
| 92 | 149 | ||
| 150 | +export_file( | ||
| 151 | + name = "ndk.sh", | ||
| 152 | + mode = "reference", | ||
| 153 | + visibility = ["PUBLIC"], | ||
| 154 | +) | ||
| 155 | + | ||
| 93 | export_file( | 156 | export_file( |
| 94 | name = "rustc.sh", | 157 | name = "rustc.sh", |
| 95 | mode = "reference", | 158 | mode = "reference", |
modified
toolchains/defs.bzl +32 -6 | @@ -22,10 +22,19 @@ def _hermetic_rust_toolchain_impl(ctx): | ||
| 22 | 22 | # rustc goes through a wrapper because the sysroot cannot travel any other |
| 23 | 23 | # way: rustc_flags takes plain strings, so an artifact path cannot be put |
| 24 | 24 | # there, and putting --sysroot on the compiler unconditionally breaks |
| 25 | - # buck's own rustc_cfg action, which passes an empty one. Both the wrapper | |
| 26 | - # and the tarball are inputs, which is the point. | |
| 25 | + # buck's own rustc_cfg action, which passes an empty one. The wrapper, the | |
| 26 | + # tarball and the sysroot are all inputs, which is the point. | |
| 27 | 27 | wrapper = ctx.attrs.wrapper[DefaultInfo].default_outputs[0] |
| 28 | - compiler = cmd_args(wrapper, dist, ctx.attrs.rustc_target_triple) | |
| 28 | + | |
| 29 | + # Cross-compiling needs a sysroot holding two triples, which no single | |
| 30 | + # archive carries, so the Android configuration passes one stitched | |
| 31 | + # together in toolchains//dist. Everything else takes the component the | |
| 32 | + # dist tarball already has for the host it runs on. | |
| 33 | + if ctx.attrs.sysroot != None: | |
| 34 | + sysroot = ctx.attrs.sysroot[DefaultInfo].default_outputs[0] | |
| 35 | + else: | |
| 36 | + sysroot = cmd_args(dist, format = "{}/rust-std-" + ctx.attrs.rustc_target_triple) | |
| 37 | + compiler = cmd_args(wrapper, dist, sysroot) | |
| 29 | 38 | rustdoc = cmd_args(dist, format = "{}/rustc/bin/rustdoc") |
| 30 | 39 | |
| 31 | 40 | return [ |
| @@ -65,6 +74,7 @@ hermetic_rust_toolchain = rule( | ||
| 65 | 74 | "rustc_binary_flags": attrs.list(attrs.arg(), default = []), |
| 66 | 75 | "rustc_flags": attrs.list(attrs.arg(), default = []), |
| 67 | 76 | "rustc_target_triple": attrs.string(), |
| 77 | + "sysroot": attrs.option(attrs.dep(providers = [DefaultInfo]), default = None), | |
| 68 | 78 | "rustc_test_flags": attrs.list(attrs.arg(), default = []), |
| 69 | 79 | "rustdoc_flags": attrs.list(attrs.arg(), default = []), |
| 70 | 80 | "warn_lints": attrs.list(attrs.string(), default = []), |
| @@ -77,8 +87,12 @@ hermetic_rust_toolchain = rule( | ||
| 77 | 87 | # The prelude's system_cxx_toolchain cannot be made hermetic: it takes its |
| 78 | 88 | # tools as plain strings, so nothing it names becomes an input, and it |
| 79 | 89 | # hardcodes nm/objcopy/objdump/ranlib/strip as bare PATH lookups regardless of |
| 80 | -# what is passed. This is the same construction with zig behind every tool that | |
| 81 | -# this graph actually runs. | |
| 90 | +# what is passed. This is the same construction with a fetched archive behind | |
| 91 | +# every tool that this graph actually runs. | |
| 92 | +# | |
| 93 | +# Which archive is the caller's business: the wrapper is asked for a mode — cc, | |
| 94 | +# c++, ar, ranlib or objcopy — and what it runs for that mode is between it and | |
| 95 | +# the dist beside it. zcc.sh answers with zig, ndk.sh with the NDK's clang. | |
| 82 | 96 | load("@prelude//cxx:cxx_toolchain_types.bzl", "BinaryUtilitiesInfo", "CCompilerInfo", "CxxCompilerInfo", "CxxInternalTools", "CxxPlatformInfo", "CxxToolchainInfo", "DepTrackingMode", "LinkerInfo", "LinkerType", "PicBehavior", "RuntimeDependencyHandling", "ShlibInterfacesMode") |
| 83 | 97 | load("@prelude//cxx:headers.bzl", "HeaderMode") |
| 84 | 98 | load("@prelude//linking:link_info.bzl", "LinkOrdering", "LinkStyle") |
| @@ -88,7 +102,14 @@ def _hermetic_cxx_toolchain_impl(ctx: AnalysisContext): | ||
| 88 | 102 | dist = ctx.attrs.dist[DefaultInfo].default_outputs[0] |
| 89 | 103 | wrapper = ctx.attrs.wrapper[DefaultInfo].default_outputs[0] |
| 90 | 104 | |
| 105 | + # A wrapper takes the directory it was given, then whatever else it needs | |
| 106 | + # to find its tools, then the mode. zcc.sh needs nothing else — zig is in | |
| 107 | + # the directory. ndk.sh is handed a manifest, because what it runs is not | |
| 108 | + # an input at all: see the note there. | |
| 91 | 109 | def zig(mode): |
| 110 | + if ctx.attrs.manifest != None: | |
| 111 | + manifest = ctx.attrs.manifest[DefaultInfo].default_outputs[0] | |
| 112 | + return RunInfo(args = cmd_args(wrapper, dist, manifest, mode)) | |
| 92 | 113 | return RunInfo(args = cmd_args(wrapper, dist, mode)) |
| 93 | 114 | |
| 94 | 115 | cc = zig("cc") |
| @@ -170,7 +191,10 @@ def _hermetic_cxx_toolchain_impl(ctx: AnalysisContext): | ||
| 170 | 191 | use_dep_files = True, |
| 171 | 192 | runtime_dependency_handling = RuntimeDependencyHandling("no_symlink"), |
| 172 | 193 | ), |
| 173 | - CxxPlatformInfo(name = "x86_64"), | |
| 194 | + # Names the target architecture, not the host's: this is what the | |
| 195 | + # prelude stamps into per-platform output paths, so an Android build | |
| 196 | + # and a desktop one cannot land on the same one. | |
| 197 | + CxxPlatformInfo(name = ctx.attrs.platform_name), | |
| 174 | 198 | ] |
| 175 | 199 | |
| 176 | 200 | hermetic_cxx_toolchain = rule( |
| @@ -183,6 +207,8 @@ hermetic_cxx_toolchain = rule( | ||
| 183 | 207 | "link_flags": attrs.list(attrs.arg(), default = []), |
| 184 | 208 | "link_ordering": attrs.option(attrs.enum(LinkOrdering.values()), default = None), |
| 185 | 209 | "link_style": attrs.string(default = "shared"), |
| 210 | + "manifest": attrs.option(attrs.dep(providers = [DefaultInfo]), default = None), | |
| 211 | + "platform_name": attrs.string(default = "x86_64"), | |
| 186 | 212 | "wrapper": attrs.dep(providers = [DefaultInfo]), |
| 187 | 213 | }, |
| 188 | 214 | is_toolchain_rule = True, |
| @@ -22,10 +22,19 @@ def _hermetic_rust_toolchain_impl(ctx): | |||
| 22 | # rustc goes through a wrapper because the sysroot cannot travel any other | 22 | # rustc goes through a wrapper because the sysroot cannot travel any other |
| 23 | # way: rustc_flags takes plain strings, so an artifact path cannot be put | 23 | # way: rustc_flags takes plain strings, so an artifact path cannot be put |
| 24 | # there, and putting --sysroot on the compiler unconditionally breaks | 24 | # there, and putting --sysroot on the compiler unconditionally breaks |
| 25 | - # buck's own rustc_cfg action, which passes an empty one. Both the wrapper | 25 | + # buck's own rustc_cfg action, which passes an empty one. The wrapper, the |
| 26 | - # and the tarball are inputs, which is the point. | 26 | + # tarball and the sysroot are all inputs, which is the point. |
| 27 | wrapper = ctx.attrs.wrapper[DefaultInfo].default_outputs[0] | 27 | wrapper = ctx.attrs.wrapper[DefaultInfo].default_outputs[0] |
| 28 | - compiler = cmd_args(wrapper, dist, ctx.attrs.rustc_target_triple) | 28 | + |
| 29 | + # Cross-compiling needs a sysroot holding two triples, which no single | ||
| 30 | + # archive carries, so the Android configuration passes one stitched | ||
| 31 | + # together in toolchains//dist. Everything else takes the component the | ||
| 32 | + # dist tarball already has for the host it runs on. | ||
| 33 | + if ctx.attrs.sysroot != None: | ||
| 34 | + sysroot = ctx.attrs.sysroot[DefaultInfo].default_outputs[0] | ||
| 35 | + else: | ||
| 36 | + sysroot = cmd_args(dist, format = "{}/rust-std-" + ctx.attrs.rustc_target_triple) | ||
| 37 | + compiler = cmd_args(wrapper, dist, sysroot) | ||
| 29 | rustdoc = cmd_args(dist, format = "{}/rustc/bin/rustdoc") | 38 | rustdoc = cmd_args(dist, format = "{}/rustc/bin/rustdoc") |
| 30 | 39 | ||
| 31 | return [ | 40 | return [ |
| @@ -65,6 +74,7 @@ hermetic_rust_toolchain = rule( | |||
| 65 | "rustc_binary_flags": attrs.list(attrs.arg(), default = []), | 74 | "rustc_binary_flags": attrs.list(attrs.arg(), default = []), |
| 66 | "rustc_flags": attrs.list(attrs.arg(), default = []), | 75 | "rustc_flags": attrs.list(attrs.arg(), default = []), |
| 67 | "rustc_target_triple": attrs.string(), | 76 | "rustc_target_triple": attrs.string(), |
| 77 | + "sysroot": attrs.option(attrs.dep(providers = [DefaultInfo]), default = None), | ||
| 68 | "rustc_test_flags": attrs.list(attrs.arg(), default = []), | 78 | "rustc_test_flags": attrs.list(attrs.arg(), default = []), |
| 69 | "rustdoc_flags": attrs.list(attrs.arg(), default = []), | 79 | "rustdoc_flags": attrs.list(attrs.arg(), default = []), |
| 70 | "warn_lints": attrs.list(attrs.string(), default = []), | 80 | "warn_lints": attrs.list(attrs.string(), default = []), |
| @@ -77,8 +87,12 @@ hermetic_rust_toolchain = rule( | |||
| 77 | # The prelude's system_cxx_toolchain cannot be made hermetic: it takes its | 87 | # The prelude's system_cxx_toolchain cannot be made hermetic: it takes its |
| 78 | # tools as plain strings, so nothing it names becomes an input, and it | 88 | # tools as plain strings, so nothing it names becomes an input, and it |
| 79 | # hardcodes nm/objcopy/objdump/ranlib/strip as bare PATH lookups regardless of | 89 | # hardcodes nm/objcopy/objdump/ranlib/strip as bare PATH lookups regardless of |
| 80 | -# what is passed. This is the same construction with zig behind every tool that | 90 | +# what is passed. This is the same construction with a fetched archive behind |
| 81 | -# this graph actually runs. | 91 | +# every tool that this graph actually runs. |
| 92 | +# | ||
| 93 | +# Which archive is the caller's business: the wrapper is asked for a mode — cc, | ||
| 94 | +# c++, ar, ranlib or objcopy — and what it runs for that mode is between it and | ||
| 95 | +# the dist beside it. zcc.sh answers with zig, ndk.sh with the NDK's clang. | ||
| 82 | load("@prelude//cxx:cxx_toolchain_types.bzl", "BinaryUtilitiesInfo", "CCompilerInfo", "CxxCompilerInfo", "CxxInternalTools", "CxxPlatformInfo", "CxxToolchainInfo", "DepTrackingMode", "LinkerInfo", "LinkerType", "PicBehavior", "RuntimeDependencyHandling", "ShlibInterfacesMode") | 96 | load("@prelude//cxx:cxx_toolchain_types.bzl", "BinaryUtilitiesInfo", "CCompilerInfo", "CxxCompilerInfo", "CxxInternalTools", "CxxPlatformInfo", "CxxToolchainInfo", "DepTrackingMode", "LinkerInfo", "LinkerType", "PicBehavior", "RuntimeDependencyHandling", "ShlibInterfacesMode") |
| 83 | load("@prelude//cxx:headers.bzl", "HeaderMode") | 97 | load("@prelude//cxx:headers.bzl", "HeaderMode") |
| 84 | load("@prelude//linking:link_info.bzl", "LinkOrdering", "LinkStyle") | 98 | load("@prelude//linking:link_info.bzl", "LinkOrdering", "LinkStyle") |
| @@ -88,7 +102,14 @@ def _hermetic_cxx_toolchain_impl(ctx: AnalysisContext): | |||
| 88 | dist = ctx.attrs.dist[DefaultInfo].default_outputs[0] | 102 | dist = ctx.attrs.dist[DefaultInfo].default_outputs[0] |
| 89 | wrapper = ctx.attrs.wrapper[DefaultInfo].default_outputs[0] | 103 | wrapper = ctx.attrs.wrapper[DefaultInfo].default_outputs[0] |
| 90 | 104 | ||
| 105 | + # A wrapper takes the directory it was given, then whatever else it needs | ||
| 106 | + # to find its tools, then the mode. zcc.sh needs nothing else — zig is in | ||
| 107 | + # the directory. ndk.sh is handed a manifest, because what it runs is not | ||
| 108 | + # an input at all: see the note there. | ||
| 91 | def zig(mode): | 109 | def zig(mode): |
| 110 | + if ctx.attrs.manifest != None: | ||
| 111 | + manifest = ctx.attrs.manifest[DefaultInfo].default_outputs[0] | ||
| 112 | + return RunInfo(args = cmd_args(wrapper, dist, manifest, mode)) | ||
| 92 | return RunInfo(args = cmd_args(wrapper, dist, mode)) | 113 | return RunInfo(args = cmd_args(wrapper, dist, mode)) |
| 93 | 114 | ||
| 94 | cc = zig("cc") | 115 | cc = zig("cc") |
| @@ -170,7 +191,10 @@ def _hermetic_cxx_toolchain_impl(ctx: AnalysisContext): | |||
| 170 | use_dep_files = True, | 191 | use_dep_files = True, |
| 171 | runtime_dependency_handling = RuntimeDependencyHandling("no_symlink"), | 192 | runtime_dependency_handling = RuntimeDependencyHandling("no_symlink"), |
| 172 | ), | 193 | ), |
| 173 | - CxxPlatformInfo(name = "x86_64"), | 194 | + # Names the target architecture, not the host's: this is what the |
| 195 | + # prelude stamps into per-platform output paths, so an Android build | ||
| 196 | + # and a desktop one cannot land on the same one. | ||
| 197 | + CxxPlatformInfo(name = ctx.attrs.platform_name), | ||
| 174 | ] | 198 | ] |
| 175 | 199 | ||
| 176 | hermetic_cxx_toolchain = rule( | 200 | hermetic_cxx_toolchain = rule( |
| @@ -183,6 +207,8 @@ hermetic_cxx_toolchain = rule( | |||
| 183 | "link_flags": attrs.list(attrs.arg(), default = []), | 207 | "link_flags": attrs.list(attrs.arg(), default = []), |
| 184 | "link_ordering": attrs.option(attrs.enum(LinkOrdering.values()), default = None), | 208 | "link_ordering": attrs.option(attrs.enum(LinkOrdering.values()), default = None), |
| 185 | "link_style": attrs.string(default = "shared"), | 209 | "link_style": attrs.string(default = "shared"), |
| 210 | + "manifest": attrs.option(attrs.dep(providers = [DefaultInfo]), default = None), | ||
| 211 | + "platform_name": attrs.string(default = "x86_64"), | ||
| 186 | "wrapper": attrs.dep(providers = [DefaultInfo]), | 212 | "wrapper": attrs.dep(providers = [DefaultInfo]), |
| 187 | }, | 213 | }, |
| 188 | is_toolchain_rule = True, | 214 | is_toolchain_rule = True, |
modified
toolchains/dist.bzl +56 -1 | @@ -19,7 +19,7 @@ def _archive(name, tool, platform): | ||
| 19 | 19 | name = name, |
| 20 | 20 | urls = [entry["url"]], |
| 21 | 21 | sha256 = entry["sha256"], |
| 22 | - strip_prefix = entry["strip_prefix"], | |
| 22 | + strip_prefix = entry["strip_prefix"] or None, | |
| 23 | 23 | type = entry["type"], |
| 24 | 24 | visibility = ["PUBLIC"], |
| 25 | 25 | ) |
| @@ -31,3 +31,58 @@ def rust_dist(name, platform): | ||
| 31 | 31 | def zig_dist(name, platform): |
| 32 | 32 | """zig, which is the C and C++ compiler, the linker and the archiver.""" |
| 33 | 33 | _archive(name, "zig", platform) |
| 34 | + | |
| 35 | +def rust_std_android_dist(name, platform): | |
| 36 | + """The Rust standard library, built for the device. | |
| 37 | + | |
| 38 | + A component rather than a toolchain: the dist tarball above carries a | |
| 39 | + libstd for the host it runs on and no other, so cross-compiling needs this | |
| 40 | + fetched beside it. | |
| 41 | + """ | |
| 42 | + _archive(name, "rust-std-android", platform) | |
| 43 | + | |
| 44 | +def rust_android_sysroot(name, rust, rust_std, host_triple, target_triple): | |
| 45 | + """The two libstds above, as the one sysroot rustc will take. | |
| 46 | + | |
| 47 | + rustc resolves a target's libstd from sysroot/lib/rustlib/<triple>, and | |
| 48 | + accepts --sysroot once. Cross-compiling therefore needs a sysroot holding | |
| 49 | + both triples: the host's, for build scripts and proc macros, and the | |
| 50 | + target's, for the crate being built. The components ship separately, so | |
| 51 | + they are stitched together here. | |
| 52 | + | |
| 53 | + scripts/rustc does this at run time, in a cache under the user's home. As | |
| 54 | + an artifact instead it is an input like anything else — hashed into the | |
| 55 | + actions that use it, and shippable to a worker. | |
| 56 | + """ | |
| 57 | + native.genrule( | |
| 58 | + name = name, | |
| 59 | + out = "sysroot", | |
| 60 | + cmd = " && ".join([ | |
| 61 | + "mkdir -p $OUT/lib", | |
| 62 | + "cp -a $(location {})/rust-std-{}/lib/. $OUT/lib/".format(rust, host_triple), | |
| 63 | + "cp -a $(location {})/rust-std-{}/lib/rustlib/{} $OUT/lib/rustlib/".format( | |
| 64 | + rust_std, | |
| 65 | + target_triple, | |
| 66 | + target_triple, | |
| 67 | + ), | |
| 68 | + ]), | |
| 69 | + visibility = ["PUBLIC"], | |
| 70 | + ) | |
| 71 | + | |
| 72 | +def dotslash_dist(name, platform): | |
| 73 | + """DotSlash, as an input, so an action can fetch what it needs where it runs. | |
| 74 | + | |
| 75 | + The NDK is the one archive this repo does not hand to buck: unpacked it is | |
| 76 | + three gigabytes, and an input that size has to reach a worker through the | |
| 77 | + CAS. Fetching it on the worker instead moves the same pinned bytes over the | |
| 78 | + same digest, from Google rather than from here. | |
| 79 | + """ | |
| 80 | + _archive(name, "dotslash", platform) | |
| 81 | + | |
| 82 | +def android_ndk_dist(name, platform): | |
| 83 | + """The NDK: clang and the LLVM binutils, over a bionic sysroot. | |
| 84 | + | |
| 85 | + The one archive here that is not a compiler for the host — it is fetched | |
| 86 | + for the host that runs it and builds only for Android. | |
| 87 | + """ | |
| 88 | + _archive(name, "android-ndk", platform) | |
| @@ -19,7 +19,7 @@ def _archive(name, tool, platform): | |||
| 19 | name = name, | 19 | name = name, |
| 20 | urls = [entry["url"]], | 20 | urls = [entry["url"]], |
| 21 | sha256 = entry["sha256"], | 21 | sha256 = entry["sha256"], |
| 22 | - strip_prefix = entry["strip_prefix"], | 22 | + strip_prefix = entry["strip_prefix"] or None, |
| 23 | type = entry["type"], | 23 | type = entry["type"], |
| 24 | visibility = ["PUBLIC"], | 24 | visibility = ["PUBLIC"], |
| 25 | ) | 25 | ) |
| @@ -31,3 +31,58 @@ def rust_dist(name, platform): | |||
| 31 | def zig_dist(name, platform): | 31 | def zig_dist(name, platform): |
| 32 | """zig, which is the C and C++ compiler, the linker and the archiver.""" | 32 | """zig, which is the C and C++ compiler, the linker and the archiver.""" |
| 33 | _archive(name, "zig", platform) | 33 | _archive(name, "zig", platform) |
| 34 | + | ||
| 35 | +def rust_std_android_dist(name, platform): | ||
| 36 | + """The Rust standard library, built for the device. | ||
| 37 | + | ||
| 38 | + A component rather than a toolchain: the dist tarball above carries a | ||
| 39 | + libstd for the host it runs on and no other, so cross-compiling needs this | ||
| 40 | + fetched beside it. | ||
| 41 | + """ | ||
| 42 | + _archive(name, "rust-std-android", platform) | ||
| 43 | + | ||
| 44 | +def rust_android_sysroot(name, rust, rust_std, host_triple, target_triple): | ||
| 45 | + """The two libstds above, as the one sysroot rustc will take. | ||
| 46 | + | ||
| 47 | + rustc resolves a target's libstd from sysroot/lib/rustlib/<triple>, and | ||
| 48 | + accepts --sysroot once. Cross-compiling therefore needs a sysroot holding | ||
| 49 | + both triples: the host's, for build scripts and proc macros, and the | ||
| 50 | + target's, for the crate being built. The components ship separately, so | ||
| 51 | + they are stitched together here. | ||
| 52 | + | ||
| 53 | + scripts/rustc does this at run time, in a cache under the user's home. As | ||
| 54 | + an artifact instead it is an input like anything else — hashed into the | ||
| 55 | + actions that use it, and shippable to a worker. | ||
| 56 | + """ | ||
| 57 | + native.genrule( | ||
| 58 | + name = name, | ||
| 59 | + out = "sysroot", | ||
| 60 | + cmd = " && ".join([ | ||
| 61 | + "mkdir -p $OUT/lib", | ||
| 62 | + "cp -a $(location {})/rust-std-{}/lib/. $OUT/lib/".format(rust, host_triple), | ||
| 63 | + "cp -a $(location {})/rust-std-{}/lib/rustlib/{} $OUT/lib/rustlib/".format( | ||
| 64 | + rust_std, | ||
| 65 | + target_triple, | ||
| 66 | + target_triple, | ||
| 67 | + ), | ||
| 68 | + ]), | ||
| 69 | + visibility = ["PUBLIC"], | ||
| 70 | + ) | ||
| 71 | + | ||
| 72 | +def dotslash_dist(name, platform): | ||
| 73 | + """DotSlash, as an input, so an action can fetch what it needs where it runs. | ||
| 74 | + | ||
| 75 | + The NDK is the one archive this repo does not hand to buck: unpacked it is | ||
| 76 | + three gigabytes, and an input that size has to reach a worker through the | ||
| 77 | + CAS. Fetching it on the worker instead moves the same pinned bytes over the | ||
| 78 | + same digest, from Google rather than from here. | ||
| 79 | + """ | ||
| 80 | + _archive(name, "dotslash", platform) | ||
| 81 | + | ||
| 82 | +def android_ndk_dist(name, platform): | ||
| 83 | + """The NDK: clang and the LLVM binutils, over a bionic sysroot. | ||
| 84 | + | ||
| 85 | + The one archive here that is not a compiler for the host — it is fetched | ||
| 86 | + for the host that runs it and builds only for Android. | ||
| 87 | + """ | ||
| 88 | + _archive(name, "android-ndk", platform) | ||
modified
toolchains/dist/BUCK +28 -1 | @@ -1,7 +1,34 @@ | ||
| 1 | 1 | # The pinned toolchains, fetched by buck rather than by the shims. See |
| 2 | 2 | # ../dist.bzl; the versions and digests come from scripts/*.dotslash. |
| 3 | -load("//:dist.bzl", "rust_dist", "zig_dist") | |
| 3 | +load( | |
| 4 | + "//:dist.bzl", | |
| 5 | + "android_ndk_dist", | |
| 6 | + "dotslash_dist", | |
| 7 | + "rust_android_sysroot", | |
| 8 | + "rust_dist", | |
| 9 | + "rust_std_android_dist", | |
| 10 | + "zig_dist", | |
| 11 | +) | |
| 4 | 12 | |
| 5 | 13 | rust_dist(name = "rust", platform = "linux-x86_64") |
| 6 | 14 | |
| 7 | 15 | zig_dist(name = "zig", platform = "linux-x86_64") |
| 16 | + | |
| 17 | +# What fetches the NDK, rather than the NDK. See dist.bzl, and ../ndk.sh for | |
| 18 | +# why the big archive is the one thing here buck does not carry. | |
| 19 | +dotslash_dist(name = "dotslash", platform = "linux-x86_64") | |
| 20 | + | |
| 21 | +# Kept for a build that would rather have the NDK as a real input than fetch it | |
| 22 | +# where it runs — nothing depends on this today. | |
| 23 | +android_ndk_dist(name = "android-ndk", platform = "linux-x86_64") | |
| 24 | + | |
| 25 | +rust_std_android_dist(name = "rust-std-android", platform = "linux-x86_64") | |
| 26 | + | |
| 27 | +# What the Rust toolchain hands rustc as --sysroot when the target is Android. | |
| 28 | +rust_android_sysroot( | |
| 29 | + name = "android-sysroot", | |
| 30 | + rust = ":rust", | |
| 31 | + rust_std = ":rust-std-android", | |
| 32 | + host_triple = "x86_64-unknown-linux-gnu", | |
| 33 | + target_triple = "aarch64-linux-android", | |
| 34 | +) | |
| @@ -1,7 +1,34 @@ | |||
| 1 | # The pinned toolchains, fetched by buck rather than by the shims. See | 1 | # The pinned toolchains, fetched by buck rather than by the shims. See |
| 2 | # ../dist.bzl; the versions and digests come from scripts/*.dotslash. | 2 | # ../dist.bzl; the versions and digests come from scripts/*.dotslash. |
| 3 | -load("//:dist.bzl", "rust_dist", "zig_dist") | 3 | +load( |
| 4 | + "//:dist.bzl", | ||
| 5 | + "android_ndk_dist", | ||
| 6 | + "dotslash_dist", | ||
| 7 | + "rust_android_sysroot", | ||
| 8 | + "rust_dist", | ||
| 9 | + "rust_std_android_dist", | ||
| 10 | + "zig_dist", | ||
| 11 | +) | ||
| 4 | 12 | ||
| 5 | rust_dist(name = "rust", platform = "linux-x86_64") | 13 | rust_dist(name = "rust", platform = "linux-x86_64") |
| 6 | 14 | ||
| 7 | zig_dist(name = "zig", platform = "linux-x86_64") | 15 | zig_dist(name = "zig", platform = "linux-x86_64") |
| 16 | + | ||
| 17 | +# What fetches the NDK, rather than the NDK. See dist.bzl, and ../ndk.sh for | ||
| 18 | +# why the big archive is the one thing here buck does not carry. | ||
| 19 | +dotslash_dist(name = "dotslash", platform = "linux-x86_64") | ||
| 20 | + | ||
| 21 | +# Kept for a build that would rather have the NDK as a real input than fetch it | ||
| 22 | +# where it runs — nothing depends on this today. | ||
| 23 | +android_ndk_dist(name = "android-ndk", platform = "linux-x86_64") | ||
| 24 | + | ||
| 25 | +rust_std_android_dist(name = "rust-std-android", platform = "linux-x86_64") | ||
| 26 | + | ||
| 27 | +# What the Rust toolchain hands rustc as --sysroot when the target is Android. | ||
| 28 | +rust_android_sysroot( | ||
| 29 | + name = "android-sysroot", | ||
| 30 | + rust = ":rust", | ||
| 31 | + rust_std = ":rust-std-android", | ||
| 32 | + host_triple = "x86_64-unknown-linux-gnu", | ||
| 33 | + target_triple = "aarch64-linux-android", | ||
| 34 | +) | ||
modified
toolchains/dist/generated.bzl +72 -0 | @@ -4,6 +4,78 @@ | ||
| 4 | 4 | # Bump the DotSlash file and run `just sync-dist`. |
| 5 | 5 | |
| 6 | 6 | DIST = { |
| 7 | + "android-ndk": { | |
| 8 | + "linux-x86_64": { | |
| 9 | + "url": "https://dl.google.com/android/repository/android-ndk-r29-linux.zip", | |
| 10 | + "sha256": "4abbbcdc842f3d4879206e9695d52709603e52dd68d3c1fff04b3b5e7a308ecf", | |
| 11 | + "strip_prefix": "android-ndk-r29", | |
| 12 | + "type": "zip" | |
| 13 | + }, | |
| 14 | + "macos-aarch64": { | |
| 15 | + "url": "https://dl.google.com/android/repository/android-ndk-r29-darwin.zip", | |
| 16 | + "sha256": "ce5e4b100ec5fe5be4eb3edcb2c02528824ff9cda3860f5304619be6c3da34d3", | |
| 17 | + "strip_prefix": "android-ndk-r29", | |
| 18 | + "type": "zip" | |
| 19 | + }, | |
| 20 | + "macos-x86_64": { | |
| 21 | + "url": "https://dl.google.com/android/repository/android-ndk-r29-darwin.zip", | |
| 22 | + "sha256": "ce5e4b100ec5fe5be4eb3edcb2c02528824ff9cda3860f5304619be6c3da34d3", | |
| 23 | + "strip_prefix": "android-ndk-r29", | |
| 24 | + "type": "zip" | |
| 25 | + } | |
| 26 | + }, | |
| 27 | + "dotslash": { | |
| 28 | + "linux-aarch64": { | |
| 29 | + "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-linux-musl.arm64.v0.5.9.tar.gz", | |
| 30 | + "sha256": "e58374446dacd4a228388cd275d974bbd8f946916b513cf0eb86bdff5d9d675e", | |
| 31 | + "strip_prefix": "", | |
| 32 | + "type": "tar.gz" | |
| 33 | + }, | |
| 34 | + "linux-x86_64": { | |
| 35 | + "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-linux-musl.x86_64.v0.5.9.tar.gz", | |
| 36 | + "sha256": "4c75c6eb7890ae35993b962073f6d9bbe78b42b81a5691303ad70f63bfbf7196", | |
| 37 | + "strip_prefix": "", | |
| 38 | + "type": "tar.gz" | |
| 39 | + }, | |
| 40 | + "macos-aarch64": { | |
| 41 | + "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-macos-arm64.v0.5.9.tar.gz", | |
| 42 | + "sha256": "eef15be1b554de7b1409cb0c69c24f2cd04f19c0f101ad69fcdf9d66d4d51639", | |
| 43 | + "strip_prefix": "", | |
| 44 | + "type": "tar.gz" | |
| 45 | + }, | |
| 46 | + "macos-x86_64": { | |
| 47 | + "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-macos-amd64.v0.5.9.tar.gz", | |
| 48 | + "sha256": "51446593596ffe4cd22bc43a0fe6d82dd367758eb53cb4ade36d6a684ed08469", | |
| 49 | + "strip_prefix": "", | |
| 50 | + "type": "tar.gz" | |
| 51 | + } | |
| 52 | + }, | |
| 53 | + "rust-std-android": { | |
| 54 | + "linux-aarch64": { | |
| 55 | + "url": "https://static.rust-lang.org/dist/2026-08-20/rust-std-1.98.0-aarch64-linux-android.tar.gz", | |
| 56 | + "sha256": "ac1be298e0500438090baa492ac8858473466e386f780878d65d42e575679514", | |
| 57 | + "strip_prefix": "rust-std-1.98.0-aarch64-linux-android", | |
| 58 | + "type": "tar.gz" | |
| 59 | + }, | |
| 60 | + "linux-x86_64": { | |
| 61 | + "url": "https://static.rust-lang.org/dist/2026-08-20/rust-std-1.98.0-aarch64-linux-android.tar.gz", | |
| 62 | + "sha256": "ac1be298e0500438090baa492ac8858473466e386f780878d65d42e575679514", | |
| 63 | + "strip_prefix": "rust-std-1.98.0-aarch64-linux-android", | |
| 64 | + "type": "tar.gz" | |
| 65 | + }, | |
| 66 | + "macos-aarch64": { | |
| 67 | + "url": "https://static.rust-lang.org/dist/2026-08-20/rust-std-1.98.0-aarch64-linux-android.tar.gz", | |
| 68 | + "sha256": "ac1be298e0500438090baa492ac8858473466e386f780878d65d42e575679514", | |
| 69 | + "strip_prefix": "rust-std-1.98.0-aarch64-linux-android", | |
| 70 | + "type": "tar.gz" | |
| 71 | + }, | |
| 72 | + "macos-x86_64": { | |
| 73 | + "url": "https://static.rust-lang.org/dist/2026-08-20/rust-std-1.98.0-aarch64-linux-android.tar.gz", | |
| 74 | + "sha256": "ac1be298e0500438090baa492ac8858473466e386f780878d65d42e575679514", | |
| 75 | + "strip_prefix": "rust-std-1.98.0-aarch64-linux-android", | |
| 76 | + "type": "tar.gz" | |
| 77 | + } | |
| 78 | + }, | |
| 7 | 79 | "rust": { |
| 8 | 80 | "linux-aarch64": { |
| 9 | 81 | "url": "https://static.rust-lang.org/dist/2026-08-20/rust-1.98.0-aarch64-unknown-linux-gnu.tar.gz", |
| @@ -4,6 +4,78 @@ | |||
| 4 | # Bump the DotSlash file and run `just sync-dist`. | 4 | # Bump the DotSlash file and run `just sync-dist`. |
| 5 | 5 | ||
| 6 | DIST = { | 6 | DIST = { |
| 7 | + "android-ndk": { | ||
| 8 | + "linux-x86_64": { | ||
| 9 | + "url": "https://dl.google.com/android/repository/android-ndk-r29-linux.zip", | ||
| 10 | + "sha256": "4abbbcdc842f3d4879206e9695d52709603e52dd68d3c1fff04b3b5e7a308ecf", | ||
| 11 | + "strip_prefix": "android-ndk-r29", | ||
| 12 | + "type": "zip" | ||
| 13 | + }, | ||
| 14 | + "macos-aarch64": { | ||
| 15 | + "url": "https://dl.google.com/android/repository/android-ndk-r29-darwin.zip", | ||
| 16 | + "sha256": "ce5e4b100ec5fe5be4eb3edcb2c02528824ff9cda3860f5304619be6c3da34d3", | ||
| 17 | + "strip_prefix": "android-ndk-r29", | ||
| 18 | + "type": "zip" | ||
| 19 | + }, | ||
| 20 | + "macos-x86_64": { | ||
| 21 | + "url": "https://dl.google.com/android/repository/android-ndk-r29-darwin.zip", | ||
| 22 | + "sha256": "ce5e4b100ec5fe5be4eb3edcb2c02528824ff9cda3860f5304619be6c3da34d3", | ||
| 23 | + "strip_prefix": "android-ndk-r29", | ||
| 24 | + "type": "zip" | ||
| 25 | + } | ||
| 26 | + }, | ||
| 27 | + "dotslash": { | ||
| 28 | + "linux-aarch64": { | ||
| 29 | + "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-linux-musl.arm64.v0.5.9.tar.gz", | ||
| 30 | + "sha256": "e58374446dacd4a228388cd275d974bbd8f946916b513cf0eb86bdff5d9d675e", | ||
| 31 | + "strip_prefix": "", | ||
| 32 | + "type": "tar.gz" | ||
| 33 | + }, | ||
| 34 | + "linux-x86_64": { | ||
| 35 | + "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-linux-musl.x86_64.v0.5.9.tar.gz", | ||
| 36 | + "sha256": "4c75c6eb7890ae35993b962073f6d9bbe78b42b81a5691303ad70f63bfbf7196", | ||
| 37 | + "strip_prefix": "", | ||
| 38 | + "type": "tar.gz" | ||
| 39 | + }, | ||
| 40 | + "macos-aarch64": { | ||
| 41 | + "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-macos-arm64.v0.5.9.tar.gz", | ||
| 42 | + "sha256": "eef15be1b554de7b1409cb0c69c24f2cd04f19c0f101ad69fcdf9d66d4d51639", | ||
| 43 | + "strip_prefix": "", | ||
| 44 | + "type": "tar.gz" | ||
| 45 | + }, | ||
| 46 | + "macos-x86_64": { | ||
| 47 | + "url": "https://github.com/facebook/dotslash/releases/download/v0.5.9/dotslash-macos-amd64.v0.5.9.tar.gz", | ||
| 48 | + "sha256": "51446593596ffe4cd22bc43a0fe6d82dd367758eb53cb4ade36d6a684ed08469", | ||
| 49 | + "strip_prefix": "", | ||
| 50 | + "type": "tar.gz" | ||
| 51 | + } | ||
| 52 | + }, | ||
| 53 | + "rust-std-android": { | ||
| 54 | + "linux-aarch64": { | ||
| 55 | + "url": "https://static.rust-lang.org/dist/2026-08-20/rust-std-1.98.0-aarch64-linux-android.tar.gz", | ||
| 56 | + "sha256": "ac1be298e0500438090baa492ac8858473466e386f780878d65d42e575679514", | ||
| 57 | + "strip_prefix": "rust-std-1.98.0-aarch64-linux-android", | ||
| 58 | + "type": "tar.gz" | ||
| 59 | + }, | ||
| 60 | + "linux-x86_64": { | ||
| 61 | + "url": "https://static.rust-lang.org/dist/2026-08-20/rust-std-1.98.0-aarch64-linux-android.tar.gz", | ||
| 62 | + "sha256": "ac1be298e0500438090baa492ac8858473466e386f780878d65d42e575679514", | ||
| 63 | + "strip_prefix": "rust-std-1.98.0-aarch64-linux-android", | ||
| 64 | + "type": "tar.gz" | ||
| 65 | + }, | ||
| 66 | + "macos-aarch64": { | ||
| 67 | + "url": "https://static.rust-lang.org/dist/2026-08-20/rust-std-1.98.0-aarch64-linux-android.tar.gz", | ||
| 68 | + "sha256": "ac1be298e0500438090baa492ac8858473466e386f780878d65d42e575679514", | ||
| 69 | + "strip_prefix": "rust-std-1.98.0-aarch64-linux-android", | ||
| 70 | + "type": "tar.gz" | ||
| 71 | + }, | ||
| 72 | + "macos-x86_64": { | ||
| 73 | + "url": "https://static.rust-lang.org/dist/2026-08-20/rust-std-1.98.0-aarch64-linux-android.tar.gz", | ||
| 74 | + "sha256": "ac1be298e0500438090baa492ac8858473466e386f780878d65d42e575679514", | ||
| 75 | + "strip_prefix": "rust-std-1.98.0-aarch64-linux-android", | ||
| 76 | + "type": "tar.gz" | ||
| 77 | + } | ||
| 78 | + }, | ||
| 7 | "rust": { | 79 | "rust": { |
| 8 | "linux-aarch64": { | 80 | "linux-aarch64": { |
| 9 | "url": "https://static.rust-lang.org/dist/2026-08-20/rust-1.98.0-aarch64-unknown-linux-gnu.tar.gz", | 81 | "url": "https://static.rust-lang.org/dist/2026-08-20/rust-1.98.0-aarch64-unknown-linux-gnu.tar.gz", |
added
toolchains/ndk.sh +54 -0 | new file mode 100755 | ||
| @@ -0,0 +1,54 @@ | ||
| 1 | +#!/bin/sh | |
| 2 | +# The NDK, as the C toolchain for Android, fetched where the action runs. | |
| 3 | +# | |
| 4 | +# Unlike zcc.sh, the archive behind this is not an input. That is deliberate: | |
| 5 | +# unpacked it is three gigabytes, and an input has to travel to a worker | |
| 6 | +# through the CAS — uploaded from here first, then materialised there. What | |
| 7 | +# travels instead is DotSlash (under a megabyte) and the manifest naming the | |
| 8 | +# archive, and the worker fetches the same bytes under the same digest from | |
| 9 | +# Google. A machine that has already fetched it does nothing. | |
| 10 | +# | |
| 11 | +# The cost of that is honest to state: what the compiler *is* stops being part | |
| 12 | +# of the action's cache digest. The manifest is an input, so a version bump | |
| 13 | +# still invalidates; a machine whose DotSlash cache holds something else under | |
| 14 | +# that digest cannot happen, because the digest is what the cache is keyed on. | |
| 15 | +# | |
| 16 | +# $1 is the directory holding the DotSlash binary, $2 the manifest to resolve | |
| 17 | +# and $3 the mode — cc, c++, ar, ranlib or objcopy. buck passes all three. | |
| 18 | +set -e | |
| 19 | +dotslash=$1/dotslash | |
| 20 | +manifest=$2 | |
| 21 | +mode=$3 | |
| 22 | +shift 3 | |
| 23 | + | |
| 24 | +# ANDROID_NDK_HOME still wins, for a machine that has one installed. | |
| 25 | +ndk=${ANDROID_NDK_HOME:-${ANDROID_NDK_ROOT:-}} | |
| 26 | +if [ -n "$ndk" ]; then | |
| 27 | + for host in linux-x86_64 darwin-x86_64; do | |
| 28 | + if [ -d "$ndk/toolchains/llvm/prebuilt/$host/bin" ]; then | |
| 29 | + bin=$ndk/toolchains/llvm/prebuilt/$host/bin | |
| 30 | + break | |
| 31 | + fi | |
| 32 | + done | |
| 33 | + [ -n "${bin:-}" ] || { echo "toolchains/ndk.sh: no llvm prebuilt under $ndk" >&2; exit 1; } | |
| 34 | +else | |
| 35 | + # The manifest names bin/clang inside the archive; DotSlash unpacks the | |
| 36 | + # whole NDK around it, which is what the driver needs — its sysroot and its | |
| 37 | + # resource directory are found relative to the binary. | |
| 38 | + bin=$(dirname "$("$dotslash" -- fetch "$manifest")") | |
| 39 | +fi | |
| 40 | + | |
| 41 | +# API 28 is frq's min_sdk_version, and the floor for what the app needs from | |
| 42 | +# bionic. The NDK ships a wrapper script per API level that adds this flag and | |
| 43 | +# nothing else; passing it here keeps this to one executable out of the | |
| 44 | +# archive, so nothing depends on which permissions survived the unpacking. | |
| 45 | +target=${JOLT_ANDROID_TARGET:-aarch64-linux-android}${JOLT_ANDROID_API:-28} | |
| 46 | + | |
| 47 | +case $mode in | |
| 48 | + cc) exec "$bin/clang" --target="$target" "$@" ;; | |
| 49 | + c++) exec "$bin/clang++" --target="$target" "$@" ;; | |
| 50 | + ar) exec "$bin/llvm-ar" "$@" ;; | |
| 51 | + ranlib) exec "$bin/llvm-ranlib" "$@" ;; | |
| 52 | + objcopy) exec "$bin/llvm-objcopy" "$@" ;; | |
| 53 | + *) echo "toolchains/ndk.sh: unknown mode $mode" >&2; exit 1 ;; | |
| 54 | +esac | |
| new file mode 100755 | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | +#!/bin/sh | ||
| 2 | +# The NDK, as the C toolchain for Android, fetched where the action runs. | ||
| 3 | +# | ||
| 4 | +# Unlike zcc.sh, the archive behind this is not an input. That is deliberate: | ||
| 5 | +# unpacked it is three gigabytes, and an input has to travel to a worker | ||
| 6 | +# through the CAS — uploaded from here first, then materialised there. What | ||
| 7 | +# travels instead is DotSlash (under a megabyte) and the manifest naming the | ||
| 8 | +# archive, and the worker fetches the same bytes under the same digest from | ||
| 9 | +# Google. A machine that has already fetched it does nothing. | ||
| 10 | +# | ||
| 11 | +# The cost of that is honest to state: what the compiler *is* stops being part | ||
| 12 | +# of the action's cache digest. The manifest is an input, so a version bump | ||
| 13 | +# still invalidates; a machine whose DotSlash cache holds something else under | ||
| 14 | +# that digest cannot happen, because the digest is what the cache is keyed on. | ||
| 15 | +# | ||
| 16 | +# $1 is the directory holding the DotSlash binary, $2 the manifest to resolve | ||
| 17 | +# and $3 the mode — cc, c++, ar, ranlib or objcopy. buck passes all three. | ||
| 18 | +set -e | ||
| 19 | +dotslash=$1/dotslash | ||
| 20 | +manifest=$2 | ||
| 21 | +mode=$3 | ||
| 22 | +shift 3 | ||
| 23 | + | ||
| 24 | +# ANDROID_NDK_HOME still wins, for a machine that has one installed. | ||
| 25 | +ndk=${ANDROID_NDK_HOME:-${ANDROID_NDK_ROOT:-}} | ||
| 26 | +if [ -n "$ndk" ]; then | ||
| 27 | + for host in linux-x86_64 darwin-x86_64; do | ||
| 28 | + if [ -d "$ndk/toolchains/llvm/prebuilt/$host/bin" ]; then | ||
| 29 | + bin=$ndk/toolchains/llvm/prebuilt/$host/bin | ||
| 30 | + break | ||
| 31 | + fi | ||
| 32 | + done | ||
| 33 | + [ -n "${bin:-}" ] || { echo "toolchains/ndk.sh: no llvm prebuilt under $ndk" >&2; exit 1; } | ||
| 34 | +else | ||
| 35 | + # The manifest names bin/clang inside the archive; DotSlash unpacks the | ||
| 36 | + # whole NDK around it, which is what the driver needs — its sysroot and its | ||
| 37 | + # resource directory are found relative to the binary. | ||
| 38 | + bin=$(dirname "$("$dotslash" -- fetch "$manifest")") | ||
| 39 | +fi | ||
| 40 | + | ||
| 41 | +# API 28 is frq's min_sdk_version, and the floor for what the app needs from | ||
| 42 | +# bionic. The NDK ships a wrapper script per API level that adds this flag and | ||
| 43 | +# nothing else; passing it here keeps this to one executable out of the | ||
| 44 | +# archive, so nothing depends on which permissions survived the unpacking. | ||
| 45 | +target=${JOLT_ANDROID_TARGET:-aarch64-linux-android}${JOLT_ANDROID_API:-28} | ||
| 46 | + | ||
| 47 | +case $mode in | ||
| 48 | + cc) exec "$bin/clang" --target="$target" "$@" ;; | ||
| 49 | + c++) exec "$bin/clang++" --target="$target" "$@" ;; | ||
| 50 | + ar) exec "$bin/llvm-ar" "$@" ;; | ||
| 51 | + ranlib) exec "$bin/llvm-ranlib" "$@" ;; | ||
| 52 | + objcopy) exec "$bin/llvm-objcopy" "$@" ;; | ||
| 53 | + *) echo "toolchains/ndk.sh: unknown mode $mode" >&2; exit 1 ;; | ||
| 54 | +esac | ||
modified
toolchains/rustc.sh +9 -6 | @@ -6,17 +6,20 @@ | ||
| 6 | 6 | # identity is part of the action's cache digest and a remote worker can be |
| 7 | 7 | # handed the whole thing. |
| 8 | 8 | # |
| 9 | -# $1 is the directory the tarball unpacked into, $2 the target triple; buck | |
| 10 | -# passes both because neither is knowable from inside the script. | |
| 9 | +# $1 is the directory the tarball unpacked into and $2 the sysroot to use; | |
| 10 | +# buck passes both because neither is knowable from inside the script. The | |
| 11 | +# sysroot is a separate argument rather than something built from a triple | |
| 12 | +# here: cross-compiling needs two libstds stitched into one directory, and | |
| 13 | +# that stitching is an artifact of its own (toolchains//dist:android-sysroot). | |
| 11 | 14 | set -e |
| 12 | 15 | dist=$1 |
| 13 | -triple=$2 | |
| 16 | +sysroot=$2 | |
| 14 | 17 | shift 2 |
| 15 | 18 | |
| 16 | 19 | # The dist tarball keeps its components in sibling directories, so the |
| 17 | 20 | # compiler's own default sysroot (rustc/) holds no libstd — it lives under |
| 18 | -# rust-std-<triple>/. The codegen libraries are found by rpath relative to the | |
| 19 | -# binary, so only the sysroot has to be named. | |
| 21 | +# rust-std-<triple>/, which is what buck names above. The codegen libraries are | |
| 22 | +# found by rpath relative to the binary, so only the sysroot has to be named. | |
| 20 | 23 | # |
| 21 | 24 | # Callers that set their own win: buck's rustc_cfg action passes an empty |
| 22 | 25 | # --sysroot, and rustc refuses the option twice. |
| @@ -26,4 +29,4 @@ for arg do | ||
| 26 | 29 | esac |
| 27 | 30 | done |
| 28 | 31 | |
| 29 | -exec "$dist/rustc/bin/rustc" --sysroot "$dist/rust-std-$triple" "$@" | |
| 32 | +exec "$dist/rustc/bin/rustc" --sysroot "$sysroot" "$@" | |
| @@ -6,17 +6,20 @@ | |||
| 6 | # identity is part of the action's cache digest and a remote worker can be | 6 | # identity is part of the action's cache digest and a remote worker can be |
| 7 | # handed the whole thing. | 7 | # handed the whole thing. |
| 8 | # | 8 | # |
| 9 | -# $1 is the directory the tarball unpacked into, $2 the target triple; buck | 9 | +# $1 is the directory the tarball unpacked into and $2 the sysroot to use; |
| 10 | -# passes both because neither is knowable from inside the script. | 10 | +# buck passes both because neither is knowable from inside the script. The |
| 11 | +# sysroot is a separate argument rather than something built from a triple | ||
| 12 | +# here: cross-compiling needs two libstds stitched into one directory, and | ||
| 13 | +# that stitching is an artifact of its own (toolchains//dist:android-sysroot). | ||
| 11 | set -e | 14 | set -e |
| 12 | dist=$1 | 15 | dist=$1 |
| 13 | -triple=$2 | 16 | +sysroot=$2 |
| 14 | shift 2 | 17 | shift 2 |
| 15 | 18 | ||
| 16 | # The dist tarball keeps its components in sibling directories, so the | 19 | # The dist tarball keeps its components in sibling directories, so the |
| 17 | # compiler's own default sysroot (rustc/) holds no libstd — it lives under | 20 | # compiler's own default sysroot (rustc/) holds no libstd — it lives under |
| 18 | -# rust-std-<triple>/. The codegen libraries are found by rpath relative to the | 21 | +# rust-std-<triple>/, which is what buck names above. The codegen libraries are |
| 19 | -# binary, so only the sysroot has to be named. | 22 | +# found by rpath relative to the binary, so only the sysroot has to be named. |
| 20 | # | 23 | # |
| 21 | # Callers that set their own win: buck's rustc_cfg action passes an empty | 24 | # Callers that set their own win: buck's rustc_cfg action passes an empty |
| 22 | # --sysroot, and rustc refuses the option twice. | 25 | # --sysroot, and rustc refuses the option twice. |
| @@ -26,4 +29,4 @@ for arg do | |||
| 26 | esac | 29 | esac |
| 27 | done | 30 | done |
| 28 | 31 | ||
| 29 | -exec "$dist/rustc/bin/rustc" --sysroot "$dist/rust-std-$triple" "$@" | 32 | +exec "$dist/rustc/bin/rustc" --sysroot "$sysroot" "$@" |
added
toolchains/selftest/BUCK +43 -0 | new file mode 100644 | ||
| @@ -0,0 +1,43 @@ | ||
| 1 | +# The Android toolchain, built. | |
| 2 | +# | |
| 3 | +# There is no Android graph in this repo yet — the third-party crates have no | |
| 4 | +# Android platform in reindeer.toml, so nothing that depends on them can be | |
| 5 | +# configured for the device — so these are what keep the toolchain honest in | |
| 6 | +# the meantime. One C object, compiled by the NDK's clang against bionic's | |
| 7 | +# headers, and one Rust one, compiled against the device's libstd and linked | |
| 8 | +# by that same clang. Both configured for the device, neither shipping. | |
| 9 | +# | |
| 10 | +# just buck build toolchains//selftest:abi-android | |
| 11 | +# just buck build toolchains//selftest:std-android | |
| 12 | +cxx_library( | |
| 13 | + name = "abi", | |
| 14 | + srcs = ["abi.c"], | |
| 15 | + visibility = ["PUBLIC"], | |
| 16 | +) | |
| 17 | + | |
| 18 | +# The target configuration moves; the execution platform does not. Building | |
| 19 | +# `:abi` directly would compile it for the host, where it is meant to fail. | |
| 20 | +configured_alias( | |
| 21 | + name = "abi-android", | |
| 22 | + actual = ":abi", | |
| 23 | + platform = "root//platforms:android-arm64", | |
| 24 | + visibility = ["PUBLIC"], | |
| 25 | +) | |
| 26 | + | |
| 27 | +rust_library( | |
| 28 | + name = "std", | |
| 29 | + srcs = ["abi.rs"], | |
| 30 | + crate_root = "abi.rs", | |
| 31 | + edition = "2021", | |
| 32 | + # Shared, so the link runs: a rlib would prove rustc found the target | |
| 33 | + # libstd and nothing about whether the linker can produce a device object. | |
| 34 | + preferred_linkage = "shared", | |
| 35 | + visibility = ["PUBLIC"], | |
| 36 | +) | |
| 37 | + | |
| 38 | +configured_alias( | |
| 39 | + name = "std-android", | |
| 40 | + actual = ":std", | |
| 41 | + platform = "root//platforms:android-arm64", | |
| 42 | + visibility = ["PUBLIC"], | |
| 43 | +) | |
| new file mode 100644 | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | +# The Android toolchain, built. | ||
| 2 | +# | ||
| 3 | +# There is no Android graph in this repo yet — the third-party crates have no | ||
| 4 | +# Android platform in reindeer.toml, so nothing that depends on them can be | ||
| 5 | +# configured for the device — so these are what keep the toolchain honest in | ||
| 6 | +# the meantime. One C object, compiled by the NDK's clang against bionic's | ||
| 7 | +# headers, and one Rust one, compiled against the device's libstd and linked | ||
| 8 | +# by that same clang. Both configured for the device, neither shipping. | ||
| 9 | +# | ||
| 10 | +# just buck build toolchains//selftest:abi-android | ||
| 11 | +# just buck build toolchains//selftest:std-android | ||
| 12 | +cxx_library( | ||
| 13 | + name = "abi", | ||
| 14 | + srcs = ["abi.c"], | ||
| 15 | + visibility = ["PUBLIC"], | ||
| 16 | +) | ||
| 17 | + | ||
| 18 | +# The target configuration moves; the execution platform does not. Building | ||
| 19 | +# `:abi` directly would compile it for the host, where it is meant to fail. | ||
| 20 | +configured_alias( | ||
| 21 | + name = "abi-android", | ||
| 22 | + actual = ":abi", | ||
| 23 | + platform = "root//platforms:android-arm64", | ||
| 24 | + visibility = ["PUBLIC"], | ||
| 25 | +) | ||
| 26 | + | ||
| 27 | +rust_library( | ||
| 28 | + name = "std", | ||
| 29 | + srcs = ["abi.rs"], | ||
| 30 | + crate_root = "abi.rs", | ||
| 31 | + edition = "2021", | ||
| 32 | + # Shared, so the link runs: a rlib would prove rustc found the target | ||
| 33 | + # libstd and nothing about whether the linker can produce a device object. | ||
| 34 | + preferred_linkage = "shared", | ||
| 35 | + visibility = ["PUBLIC"], | ||
| 36 | +) | ||
| 37 | + | ||
| 38 | +configured_alias( | ||
| 39 | + name = "std-android", | ||
| 40 | + actual = ":std", | ||
| 41 | + platform = "root//platforms:android-arm64", | ||
| 42 | + visibility = ["PUBLIC"], | ||
| 43 | +) | ||
added
toolchains/selftest/abi.c +26 -0 | new file mode 100644 | ||
| @@ -0,0 +1,26 @@ | ||
| 1 | +/* What the Android toolchain is asked to prove. | |
| 2 | + * | |
| 3 | + * Nothing ships this: it is the smallest object that can only compile if the | |
| 4 | + * driver really is the NDK's, really is pointed at bionic, and really is | |
| 5 | + * building for the device rather than for the host that invoked it. A desktop | |
| 6 | + * clang answers every one of these the other way, and __ANDROID_API__ comes | |
| 7 | + * from the --target= suffix, so a missing API level fails here too. */ | |
| 8 | +#include <android/api-level.h> | |
| 9 | +#include <jni.h> | |
| 10 | + | |
| 11 | +#if !defined(__ANDROID__) | |
| 12 | +#error "not an Android target" | |
| 13 | +#endif | |
| 14 | +#if !defined(__aarch64__) | |
| 15 | +#error "not an arm64 target" | |
| 16 | +#endif | |
| 17 | +#if __ANDROID_API__ < 28 | |
| 18 | +#error "API level below frq's min_sdk_version" | |
| 19 | +#endif | |
| 20 | + | |
| 21 | +/* jni.h is a platform header, so reaching it proves the sysroot is the NDK's | |
| 22 | + * and not whatever the host has under /usr/include. */ | |
| 23 | +jint jolt_android_abi_ok(void) { | |
| 24 | + return __ANDROID_API__; | |
| 25 | +} | |
| 26 | + | |
| new file mode 100644 | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | +/* What the Android toolchain is asked to prove. | ||
| 2 | + * | ||
| 3 | + * Nothing ships this: it is the smallest object that can only compile if the | ||
| 4 | + * driver really is the NDK's, really is pointed at bionic, and really is | ||
| 5 | + * building for the device rather than for the host that invoked it. A desktop | ||
| 6 | + * clang answers every one of these the other way, and __ANDROID_API__ comes | ||
| 7 | + * from the --target= suffix, so a missing API level fails here too. */ | ||
| 8 | +#include <android/api-level.h> | ||
| 9 | +#include <jni.h> | ||
| 10 | + | ||
| 11 | +#if !defined(__ANDROID__) | ||
| 12 | +#error "not an Android target" | ||
| 13 | +#endif | ||
| 14 | +#if !defined(__aarch64__) | ||
| 15 | +#error "not an arm64 target" | ||
| 16 | +#endif | ||
| 17 | +#if __ANDROID_API__ < 28 | ||
| 18 | +#error "API level below frq's min_sdk_version" | ||
| 19 | +#endif | ||
| 20 | + | ||
| 21 | +/* jni.h is a platform header, so reaching it proves the sysroot is the NDK's | ||
| 22 | + * and not whatever the host has under /usr/include. */ | ||
| 23 | +jint jolt_android_abi_ok(void) { | ||
| 24 | + return __ANDROID_API__; | ||
| 25 | +} | ||
| 26 | + | ||
added
toolchains/selftest/abi.rs +22 -0 | new file mode 100644 | ||
| @@ -0,0 +1,22 @@ | ||
| 1 | +//! The Rust half of what the Android toolchain is asked to prove. | |
| 2 | +//! | |
| 3 | +//! No dependencies, first-party or third: the third-party graph has no Android | |
| 4 | +//! platform yet, so this is what can cross-compile today. It fails to build | |
| 5 | +//! unless rustc was given the device's libstd — `std::os::android` exists on | |
| 6 | +//! no other target — and unless the sysroot also still holds the host's, which | |
| 7 | +//! is what the stitching in toolchains//dist is for. | |
| 8 | + | |
| 9 | +#[cfg(target_os = "android")] | |
| 10 | +pub use std::os::android as _platform; | |
| 11 | + | |
| 12 | +#[cfg(not(target_os = "android"))] | |
| 13 | +compile_error!("not an Android target"); | |
| 14 | + | |
| 15 | +/// Named so the symbol is visible in the object; nothing calls it. | |
| 16 | +#[unsafe(no_mangle)] | |
| 17 | +pub extern "C" fn jolt_android_std_ok() -> u32 { | |
| 18 | + // Reaching libstd's allocator and its formatting machinery is the point: | |
| 19 | + // a target libstd that failed to load would not get this far. | |
| 20 | + format!("{}", u32::from(cfg!(target_arch = "aarch64"))).len() as u32 | |
| 21 | +} | |
| 22 | + | |
| new file mode 100644 | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | +//! The Rust half of what the Android toolchain is asked to prove. | ||
| 2 | +//! | ||
| 3 | +//! No dependencies, first-party or third: the third-party graph has no Android | ||
| 4 | +//! platform yet, so this is what can cross-compile today. It fails to build | ||
| 5 | +//! unless rustc was given the device's libstd — `std::os::android` exists on | ||
| 6 | +//! no other target — and unless the sysroot also still holds the host's, which | ||
| 7 | +//! is what the stitching in toolchains//dist is for. | ||
| 8 | + | ||
| 9 | +#[cfg(target_os = "android")] | ||
| 10 | +pub use std::os::android as _platform; | ||
| 11 | + | ||
| 12 | +#[cfg(not(target_os = "android"))] | ||
| 13 | +compile_error!("not an Android target"); | ||
| 14 | + | ||
| 15 | +/// Named so the symbol is visible in the object; nothing calls it. | ||
| 16 | +#[unsafe(no_mangle)] | ||
| 17 | +pub extern "C" fn jolt_android_std_ok() -> u32 { | ||
| 18 | + // Reaching libstd's allocator and its formatting machinery is the point: | ||
| 19 | + // a target libstd that failed to load would not get this far. | ||
| 20 | + format!("{}", u32::from(cfg!(target_arch = "aarch64"))).len() as u32 | ||
| 21 | +} | ||
| 22 | + | ||