ANDROID_PLATFORM means "link native code for API 28" to cargo-ndk, but the android-build crate reads the same variable as "compile Java against platforms/android-28/android.jar" — a directory that does not exist in the image, because only the compile SDK is installed. Slint's Android backend builds a Java helper through that crate, so it panicked with "No Android platforms found" while android.jar sat in android-36. ANDROID_JAR is checked ahead of the platform lookup and settles it: Java compiles against the compile SDK, native code still links against MIN_API. The two are meant to differ; only the variable name is overloaded. Assisted-by: LLM
147 lines
6.9 KiB
Docker
147 lines
6.9 KiB
Docker
# DarkRoom — reproducible Android build environment
|
|
#
|
|
# Pins the entire toolchain: JDK, Android SDK, NDK, Rust, and the four Android
|
|
# targets. Both CI and local builds use this image, so "works on my machine"
|
|
# and "works in CI" are the same machine.
|
|
#
|
|
# Build: podman build -t darkroom-android:latest docker/android
|
|
# Use: ./docker/android/build.sh cargo ndk -t arm64-v8a build --release
|
|
|
|
FROM docker.io/library/debian:bookworm-slim
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Versions — pinned deliberately. Bumping any of these is a reviewable change,
|
|
# not something that drifts underneath the build.
|
|
# ---------------------------------------------------------------------------
|
|
ARG JDK_VERSION=17
|
|
# Compile SDK / target API.
|
|
ARG ANDROID_API=36
|
|
# Minimum supported API — the level native code links against (NFR-COMPAT-1).
|
|
# 28 (Android 9) matches the floor where Vulkan support is dependable.
|
|
# cargo-ndk otherwise defaults to 21, which is far below what this app needs.
|
|
ARG MIN_API=28
|
|
ARG BUILD_TOOLS=36.0.0
|
|
ARG NDK_VERSION=27.2.12479018
|
|
ARG CMDLINE_TOOLS=13114758
|
|
# Must satisfy cargo-ndk's MSRV (4.1.x needs >= 1.86) as well as our own crates.
|
|
ARG RUST_VERSION=1.92.0
|
|
|
|
# JDK 17, not the host's 25: the Android Gradle Plugin supports 17 as its
|
|
# stable target, and newer JDKs regularly break Gradle in ways that cost more
|
|
# time than they save.
|
|
ENV DEBIAN_FRONTEND=noninteractive \
|
|
ANDROID_HOME=/opt/android-sdk \
|
|
ANDROID_SDK_ROOT=/opt/android-sdk \
|
|
JAVA_HOME=/usr/lib/jvm/java-${JDK_VERSION}-openjdk-amd64 \
|
|
CARGO_HOME=/opt/cargo \
|
|
RUSTUP_HOME=/opt/rustup \
|
|
PATH=/opt/cargo/bin:/opt/android-sdk/cmdline-tools/latest/bin:/opt/android-sdk/platform-tools:$PATH
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# System packages
|
|
# ---------------------------------------------------------------------------
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
ca-certificates curl unzip git \
|
|
openjdk-${JDK_VERSION}-jdk-headless \
|
|
# Slint / winit build-time needs
|
|
pkg-config libfontconfig1-dev \
|
|
# native deps that may need building for host-side tooling
|
|
build-essential cmake python3 \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Android SDK + NDK
|
|
# ---------------------------------------------------------------------------
|
|
RUN mkdir -p ${ANDROID_HOME}/cmdline-tools \
|
|
&& curl -fsSL -o /tmp/tools.zip \
|
|
"https://dl.google.com/android/repository/commandlinetools-linux-${CMDLINE_TOOLS}_latest.zip" \
|
|
&& unzip -q /tmp/tools.zip -d ${ANDROID_HOME}/cmdline-tools \
|
|
&& mv ${ANDROID_HOME}/cmdline-tools/cmdline-tools ${ANDROID_HOME}/cmdline-tools/latest \
|
|
&& rm /tmp/tools.zip
|
|
|
|
RUN yes | sdkmanager --licenses > /dev/null 2>&1 || true \
|
|
&& sdkmanager --install \
|
|
"platform-tools" \
|
|
"platforms;android-${ANDROID_API}" \
|
|
"build-tools;${BUILD_TOOLS}" \
|
|
"ndk;${NDK_VERSION}" \
|
|
> /dev/null
|
|
|
|
ENV ANDROID_NDK_HOME=${ANDROID_HOME}/ndk/${NDK_VERSION} \
|
|
ANDROID_NDK_ROOT=${ANDROID_HOME}/ndk/${NDK_VERSION}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Rust + Android targets
|
|
#
|
|
# All four ABIs. arm64-v8a covers essentially every current device; the others
|
|
# exist so an ABI-specific build break is caught here rather than at release.
|
|
# ---------------------------------------------------------------------------
|
|
RUN curl -fsSL https://sh.rustup.rs | sh -s -- \
|
|
-y --no-modify-path --profile minimal --default-toolchain ${RUST_VERSION} \
|
|
&& rustup target add \
|
|
aarch64-linux-android \
|
|
armv7-linux-androideabi \
|
|
x86_64-linux-android \
|
|
i686-linux-android \
|
|
&& rustup component add rustfmt clippy \
|
|
&& cargo install cargo-ndk --locked \
|
|
&& chmod -R a+rwX ${CARGO_HOME} ${RUSTUP_HOME}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Linker configuration
|
|
#
|
|
# cargo-ndk normally handles this, but setting it explicitly means plain
|
|
# `cargo build --target …` works too, which matters for tooling that shells
|
|
# out to cargo directly (rust-analyzer, cargo-metadata).
|
|
# ---------------------------------------------------------------------------
|
|
ENV NDK_BIN=${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin
|
|
|
|
# Linkers target MIN_API, not ANDROID_API — the binary must run on the oldest
|
|
# supported device, while the SDK compiles against the newest.
|
|
ENV CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=${NDK_BIN}/aarch64-linux-android${MIN_API}-clang \
|
|
CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER=${NDK_BIN}/armv7a-linux-androideabi${MIN_API}-clang \
|
|
CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER=${NDK_BIN}/x86_64-linux-android${MIN_API}-clang \
|
|
CARGO_TARGET_I686_LINUX_ANDROID_LINKER=${NDK_BIN}/i686-linux-android${MIN_API}-clang
|
|
|
|
# cargo-ndk reads this; without it it defaults to API 21.
|
|
ENV CARGO_NDK_PLATFORM=${MIN_API} \
|
|
ANDROID_PLATFORM=${MIN_API}
|
|
|
|
# ANDROID_PLATFORM above means "link native code for API 28" to cargo-ndk, but
|
|
# the android-build crate reads the same variable as "compile Java against
|
|
# platforms/android-28/android.jar" — a directory that does not exist here,
|
|
# because only the compile SDK (ANDROID_API) is installed. Slint's Android
|
|
# backend builds a Java helper through that crate, so it panics with
|
|
# "No Android platforms found" while android.jar sits in android-36.
|
|
#
|
|
# ANDROID_JAR is checked ahead of the platform lookup and settles it: Java
|
|
# compiles against the compile SDK, native code still links against MIN_API.
|
|
# The two are meant to differ (see the README's compile-SDK-versus-min-API
|
|
# note); only the variable name is overloaded.
|
|
ENV ANDROID_JAR=${ANDROID_HOME}/platforms/android-${ANDROID_API}/android.jar
|
|
|
|
# Crates with C or assembly components (ring's crypto core, and anything else
|
|
# using the cc crate) need a compiler and archiver per target, not just a
|
|
# linker. cargo-ndk sets the linker only, so these are set explicitly —
|
|
# otherwise `ring` fails its build script and TLS cannot be built at all.
|
|
ENV CC_aarch64_linux_android=${NDK_BIN}/aarch64-linux-android${MIN_API}-clang \
|
|
AR_aarch64_linux_android=${NDK_BIN}/llvm-ar \
|
|
CC_armv7_linux_androideabi=${NDK_BIN}/armv7a-linux-androideabi${MIN_API}-clang \
|
|
AR_armv7_linux_androideabi=${NDK_BIN}/llvm-ar \
|
|
CC_x86_64_linux_android=${NDK_BIN}/x86_64-linux-android${MIN_API}-clang \
|
|
AR_x86_64_linux_android=${NDK_BIN}/llvm-ar \
|
|
CC_i686_linux_android=${NDK_BIN}/i686-linux-android${MIN_API}-clang \
|
|
AR_i686_linux_android=${NDK_BIN}/llvm-ar
|
|
|
|
# Shared cargo registry cache — bind-mount over this to persist across runs.
|
|
VOLUME ["/opt/cargo/registry"]
|
|
|
|
WORKDIR /work
|
|
|
|
# Rootless podman maps the host user into the container, so the image must not
|
|
# assume a fixed uid. Keep world-writable toolchain dirs and let the caller
|
|
# pass --user.
|
|
RUN chmod -R a+rwX ${ANDROID_HOME}
|
|
|
|
CMD ["/bin/bash"]
|