Adding first APK build

This commit is contained in:
ml
2026-07-20 09:45:08 +02:00
parent 8712269b51
commit 9e2c9ffbe6
42 changed files with 10012 additions and 6 deletions
+130
View File
@@ -24,6 +24,29 @@ ARG SOUNDFONT_URL=http://rancid.kapsi.fi/windows.sf2
# by anything else.
ARG NODE_VERSION=20.18.1
# Android layer, for the Quest APK build (Quest 2, 3, 3S, Pro - all
# arm64-v8a, see build-apk.sh). Same SDL2 major version as the desktop
# build above, but SDL2/SDL2_mixer are much newer releases here - unlike
# the desktop autotools build, Android needs their real CMake+NDK build
# support, which only landed in later releases. targetSdkVersion 29 (not
# compileSdk, which can and does stay newer) is deliberate: paired with
# requestLegacyExternalStorage in the manifest, it keeps /sdcard a plain,
# unrestricted shared folder instead of hitting Android 11+ scoped
# storage - this is keyed off the *app's* targetSdkVersion, not the
# device's own Android version, so it keeps working on newer Quest
# hardware/OS updates too, not just whatever's current today.
ARG ANDROID_SDL2_VERSION=2.28.5
ARG ANDROID_SDL2_MIXER_VERSION=2.8.0
ARG ANDROID_CMDLINE_TOOLS_VERSION=11076708
ARG ANDROID_PLATFORM_VERSION=29
# compileSdk in android/app/build.gradle - separate from the app's own
# targetSdkVersion (29, above): compileSdk is just which android.jar the
# app compiles against, and androidx.core needs a newer one than 29.
ARG ANDROID_COMPILE_SDK_VERSION=34
ARG ANDROID_BUILD_TOOLS_VERSION=34.0.0
ARG ANDROID_NDK_VERSION=26.1.10909125
ARG ANDROID_CMAKE_VERSION=3.22.1
ENV DEBIAN_FRONTEND=noninteractive
# build-essential/cmake/make: engine/ itself (CMake) and SDL2/SDL2_mixer
@@ -41,11 +64,21 @@ ENV DEBIAN_FRONTEND=noninteractive
# libogg-dev/libvorbis-dev: SDL2_mixer's OGG Vorbis music decoder.
# libasound2-dev: engine/CMakeLists.txt's optional native ALSA MIDI output.
# openssh-client: the CI workflow's `sftp` publish step.
# openjdk-17-jdk-headless: Gradle/AGP's own minimum JDK for the APK build.
# unzip: extracts the Android cmdline-tools zip below.
#
# All apt installs deliberately live in this one RUN, first, so editing
# anything below it (in particular the Android cross-compile steps, the
# newest and least settled part of this file) never invalidates Docker's
# build cache for this layer - and vice versa, adding a package here only
# ever costs a rebuild of the (slow) layers below it, not a re-download
# of packages that didn't change.
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential cmake make git curl ca-certificates pkg-config gosu \
libgl1-mesa-dev libglx-dev libxext-dev libx11-dev libxrandr-dev \
libxi-dev libxfixes-dev libxss-dev libxinerama-dev libxcursor-dev \
libogg-dev libvorbis-dev libasound2-dev openssh-client \
openjdk-17-jdk-headless unzip \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /opt/prebuilt
@@ -95,6 +128,103 @@ RUN curl -sSL -o /tmp/node.tar.xz \
&& tar -xJf /tmp/node.tar.xz -C /usr/local --strip-components=1 \
&& rm /tmp/node.tar.xz
# Android SDK/NDK: the cmdline-tools' own sdkmanager installs exactly the
# platform/build-tools/NDK/CMake versions the app/build.gradle below pins.
# gradlew (invoked by build-apk.sh) resolves its own pinned Gradle version
# itself - no Gradle binary is baked into this image. Unlike the manually
# cross-compiled SDL2/SDL2_mixer/fluidsynth-lite below, `./gradlew
# assembleDebug` still needs network access at build time for Gradle/AGP's
# own dependency resolution - an accepted, deliberate exception to this
# image's otherwise-offline build philosophy (see build-apk.sh).
ENV ANDROID_HOME=/opt/android-sdk \
ANDROID_SDK_ROOT=/opt/android-sdk
ENV PATH=${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tools:${PATH}
RUN mkdir -p "${ANDROID_HOME}/cmdline-tools" \
&& curl -sSL -o /tmp/cmdline-tools.zip \
"https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_CMDLINE_TOOLS_VERSION}_latest.zip" \
&& unzip -q /tmp/cmdline-tools.zip -d "${ANDROID_HOME}/cmdline-tools" \
&& mv "${ANDROID_HOME}/cmdline-tools/cmdline-tools" "${ANDROID_HOME}/cmdline-tools/latest" \
&& rm /tmp/cmdline-tools.zip \
&& yes | sdkmanager --licenses >/dev/null \
&& sdkmanager --install \
"platform-tools" \
"platforms;android-${ANDROID_PLATFORM_VERSION}" \
"platforms;android-${ANDROID_COMPILE_SDK_VERSION}" \
"build-tools;${ANDROID_BUILD_TOOLS_VERSION}" \
"ndk;${ANDROID_NDK_VERSION}" \
"cmake;${ANDROID_CMAKE_VERSION}"
ENV ANDROID_NDK_HOME=${ANDROID_HOME}/ndk/${ANDROID_NDK_VERSION}
ENV ANDROID_NDK_TOOLCHAIN=${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake
# SDL2, SDL2_mixer, and fluidsynth-lite, cross-compiled for arm64-v8a (the
# Quest's only ABI, across Quest 2/3/3S/Pro) via the NDK's CMake toolchain file, each installed
# to its own prefix under /opt/prebuilt/android/ - mirrors the desktop
# build_ext/built_sdl / built_sdl_mixer layout, just for Android. Shipped
# in the APK as separate .so's (see build-apk.sh), loaded in that order by
# QuestShockActivity.getLibraries() before the game's own libmain.so.
ARG ANDROID_ABI=arm64-v8a
RUN curl -sSLO "https://www.libsdl.org/release/SDL2-${ANDROID_SDL2_VERSION}.tar.gz" \
&& tar xf "SDL2-${ANDROID_SDL2_VERSION}.tar.gz" \
&& cmake -S "SDL2-${ANDROID_SDL2_VERSION}" -B build-sdl2-android \
-DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_TOOLCHAIN}" \
-DANDROID_ABI="${ANDROID_ABI}" -DANDROID_PLATFORM="android-${ANDROID_PLATFORM_VERSION}" \
-DCMAKE_INSTALL_PREFIX=/opt/prebuilt/android/sdl2 -DBUILD_SHARED_LIBS=ON \
-DSDL_STATIC=OFF \
&& cmake --build build-sdl2-android -j"$(nproc)" \
&& cmake --install build-sdl2-android \
&& rm -rf "SDL2-${ANDROID_SDL2_VERSION}" "SDL2-${ANDROID_SDL2_VERSION}.tar.gz" build-sdl2-android
# All optional codecs disabled: Shockolate only ever calls
# Mix_LoadWAV_RW/Mix_HookMusic (confirmed by grep - it feeds fluidsynth's
# own PCM output through Mix_HookMusic, and never loads OGG/MOD/FLAC/MP3
# game data), so plain WAVE support (always built in, no extra
# dependency) is all that's needed - avoiding SDL2_mixer's vendored
# third-party codec libraries entirely.
RUN curl -sSLO "https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-${ANDROID_SDL2_MIXER_VERSION}.tar.gz" \
&& tar xf "SDL2_mixer-${ANDROID_SDL2_MIXER_VERSION}.tar.gz" \
&& cmake -S "SDL2_mixer-${ANDROID_SDL2_MIXER_VERSION}" -B build-sdl2mixer-android \
-DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_TOOLCHAIN}" \
-DANDROID_ABI="${ANDROID_ABI}" -DANDROID_PLATFORM="android-${ANDROID_PLATFORM_VERSION}" \
-DCMAKE_PREFIX_PATH=/opt/prebuilt/android/sdl2 \
-DCMAKE_FIND_ROOT_PATH=/opt/prebuilt/android/sdl2 \
-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH \
-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=BOTH \
-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=BOTH \
-DCMAKE_INSTALL_PREFIX=/opt/prebuilt/android/sdl2_mixer -DBUILD_SHARED_LIBS=ON \
-DSDL2MIXER_VENDORED=OFF -DSDL2MIXER_SAMPLES=OFF -DSDL2MIXER_CMD=OFF \
-DSDL2MIXER_FLAC=OFF -DSDL2MIXER_GME=OFF -DSDL2MIXER_MOD=OFF \
-DSDL2MIXER_MP3=OFF -DSDL2MIXER_MIDI=OFF -DSDL2MIXER_OPUS=OFF -DSDL2MIXER_VORBIS=OFF \
-DSDL2MIXER_WAVPACK=OFF \
&& cmake --build build-sdl2mixer-android -j"$(nproc)" \
&& cmake --install build-sdl2mixer-android \
&& rm -rf "SDL2_mixer-${ANDROID_SDL2_MIXER_VERSION}" "SDL2_mixer-${ANDROID_SDL2_MIXER_VERSION}.tar.gz" build-sdl2mixer-android
RUN git clone https://github.com/EtherTyper/fluidsynth-lite.git fluidsynth-lite-android \
&& cd fluidsynth-lite-android \
&& git checkout "${FLUIDSYNTH_LITE_REF}" \
&& sed -i 's/DLL"\ off/DLL"\ on/' CMakeLists.txt \
&& sed -i 's/-Wall -Werror -std=gnu11/-Wall -std=gnu11/' CMakeLists.txt \
&& sed -i 's/set ( LIBFLUID_LIBS m pthread )/set ( LIBFLUID_LIBS m )/' CMakeLists.txt \
&& rm -rf .git \
&& cd .. \
&& cmake -S fluidsynth-lite-android -B build-fluidsynth-android \
-DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_TOOLCHAIN}" \
-DANDROID_ABI="${ANDROID_ABI}" -DANDROID_PLATFORM="android-${ANDROID_PLATFORM_VERSION}" \
&& cmake --build build-fluidsynth-android -j"$(nproc)" \
&& mkdir -p /opt/prebuilt/android/fluidsynth-lite/lib /opt/prebuilt/android/fluidsynth-lite/include \
&& cp -a build-fluidsynth-android/src/libfluidsynth.so* /opt/prebuilt/android/fluidsynth-lite/lib/ \
&& cp -a fluidsynth-lite-android/include/. /opt/prebuilt/android/fluidsynth-lite/include/ \
# version.h is generated from version.h.in at configure time - fine on
# the desktop build (in-source, so it lands right back in the source
# tree), but this is an out-of-source build, so it only exists under
# build-fluidsynth-android/ and must be copied in separately.
&& cp -a build-fluidsynth-android/include/fluidsynth/version.h \
/opt/prebuilt/android/fluidsynth-lite/include/fluidsynth/version.h \
&& rm -rf fluidsynth-lite-android build-fluidsynth-android
COPY build-image/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
COPY build-image/build-engine.sh /usr/local/bin/build-engine.sh
RUN chmod +x /usr/local/bin/docker-entrypoint.sh /usr/local/bin/build-engine.sh