Files
OpenRoyale/tools/scripts/build_native_direct.sh
T
2026-07-09 07:40:42 +02:00

88 lines
5.4 KiB
Bash

#!/usr/bin/env bash
# build_native_direct.sh - build the required native libs for an ABI using the NDK clang DIRECTLY.
#
# We invoke the NDK's clang++ with a target triple instead of the CMake toolchain, because the NDK's
# build/cmake/ integration is not present in this checkout. Produces the three libs the app loads:
# libg.so - reconstructed Titan JNI skeleton (native/src/libg/**) [REAL reconstruction]
# libfmod.so- official arm64 binary if in native/third_party/fmod/<abi>/, else the fmod stub [stub]
# libcr.so - built from native/third_party/libcr/src if present (SONAME libmod.so), else stub [stub]
#
# Usage: build_native_direct.sh <arm64-v8a|armeabi-v7a> [minApi]
# Output: app/src/main/jniLibs/<abi>/ (for armeabi-v7a it will NOT overwrite the seeded ORIGINAL
# libs unless FORCE_V7A=1 - the original 32-bit engine is higher-fidelity than the skeleton).
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJ="$(cd "$HERE/../.." && pwd)"
ABI="${1:?usage: build_native_direct.sh <arm64-v8a|armeabi-v7a> [minApi]}"
MINAPI="${2:-21}"
NDK="${ANDROID_NDK_HOME:-$PROJ/tools/android-sdk/ndk/26.3.11579264}"
TC="$NDK/toolchains/llvm/prebuilt/linux-x86_64"
CLANGXX="$TC/bin/clang++"; NM="$TC/bin/llvm-nm"
[ -x "$CLANGXX" ] || { echo "NDK clang++ not found at $CLANGXX"; exit 2; }
case "$ABI" in
arm64-v8a) TRIPLE="aarch64-linux-android${MINAPI}";;
armeabi-v7a) TRIPLE="armv7a-linux-androideabi${MINAPI}";;
*) echo "unsupported ABI '$ABI'"; exit 1;;
esac
DEST="$PROJ/app/src/main/jniLibs/$ABI"; mkdir -p "$DEST"
B="$PROJ/.build/native/$ABI"; rm -rf "$B"; mkdir -p "$B"
# -static-libstdc++ links libc++ statically so the reconstructed libg.so is SELF-CONTAINED (no
# NEEDED libc++_shared.so). libc++_shared.so is NOT a system library on Android, and we don't bundle
# it - without static libc++ the app would UnsatisfiedLinkError at load. The original engine linked
# the on-device system libstdc++.so; static libc++ is the modern, bundle-free equivalent.
CXXFLAGS=(--target="$TRIPLE" -fPIC -shared -std=c++17 -O2 -fexceptions -frtti -static-libstdc++
-I "$PROJ/native/include" -I "$PROJ/native/third_party/fmod/include")
echo "==> Native build $ABI ($TRIPLE), NDK r26d clang"
# --- libg: the reconstructed engine JNI skeleton (always rebuilt) ---
# The FMOD 1.05.11 headers are on the include path so the reconstructed audio subsystem compiles.
# We link the real libfmod.so ONLY once libg actually references FMOD (matching the original's
# NEEDED libfmod.so). NOTE: the old FMOD .so has a legacy symbol table modern lld rejects for direct
# linking, so when FMOD calls appear we link via a clean import stub (native/third_party/fmod/import)
# generated by gen_fmod_import.sh; at runtime the real libfmod.so resolves them.
mapfile -t LIBG_SRC < <(find "$PROJ/native/src/libg" -name '*.cpp')
FMOD_LINK=()
if grep -rqlE '\bFMOD_|FMOD::' "$PROJ/native/src/libg" 2>/dev/null; then
IMPORT="$PROJ/native/third_party/fmod/import/$ABI/libfmod.so"
# auto-generate the link-clean FMOD import stub if missing (and the real lib is present)
if [ ! -f "$IMPORT" ] && [ -f "$PROJ/native/third_party/fmod/$ABI/libfmod.so" ]; then
ANDROID_NDK_HOME="$NDK" bash "$HERE/gen_fmod_import.sh" "$ABI" >/dev/null 2>&1 || true
fi
if [ -f "$IMPORT" ]; then FMOD_LINK=(-L "$PROJ/native/third_party/fmod/import/$ABI" -lfmod); \
else echo " (WARN: libg references FMOD but no import stub could be generated)"; fi
fi
"$CLANGXX" "${CXXFLAGS[@]}" "${LIBG_SRC[@]}" "${FMOD_LINK[@]}" -llog -landroid -o "$B/libg.so"
echo " libg.so : $("$NM" -D --defined-only "$B/libg.so" | grep -cE 'Java_com_supercell|JNI_OnLoad') JNI exports"
# --- libfmod: official binary if present, else stub ---
if [ -f "$PROJ/native/third_party/fmod/$ABI/libfmod.so" ]; then
cp "$PROJ/native/third_party/fmod/$ABI/libfmod.so" "$B/libfmod.so"; echo " libfmod.so: official arm64 binary"
else
"$CLANGXX" "${CXXFLAGS[@]}" -Wl,-soname,libfmod.so \
"$PROJ/native/compatibility/fmod_shim/fmod_stub.cpp" -llog -o "$B/libfmod.so"; echo " libfmod.so: STUB (audio disabled)"
fi
# --- libcr: OPTIONAL. Built ONLY if the OWNED source is present (SONAME libmod.so like the original).
# If no source is provided, libcr is simply omitted - the app's System.loadLibrary("cr") is wrapped in
# catch(UnsatisfiedLinkError), so a missing libcr is tolerated (mod features just absent). ---
mapfile -t LIBCR_SRC < <(find "$PROJ/native/third_party/libcr/src" \( -name '*.c' -o -name '*.cpp' \) 2>/dev/null || true)
if [ "${#LIBCR_SRC[@]}" -gt 0 ]; then
"$CLANGXX" "${CXXFLAGS[@]}" -Wl,-soname,libmod.so -I "$PROJ/native/third_party/libcr/include" \
"${LIBCR_SRC[@]}" -llog -lz -landroid -o "$B/libcr.so"; echo " libcr.so : built from OWNED source"
else
echo " libcr.so : (omitted - no source in native/third_party/libcr/src; loadLibrary(\"cr\") is optional)"
fi
# --- publish to jniLibs ---
if [ "$ABI" = armeabi-v7a ] && [ -f "$DEST/libg.so" ] && [ "${FORCE_V7A:-0}" != 1 ]; then
echo " (armeabi-v7a: keeping seeded ORIGINAL libs in jniLibs; set FORCE_V7A=1 to overwrite with reconstruction)"
else
# We fully own this ABI dir - clear the libs we manage so a now-omitted optional lib (e.g. libcr
# when no source is present) does not linger from a previous run.
rm -f "$DEST/libg.so" "$DEST/libfmod.so" "$DEST/libcr.so"
cp "$B"/*.so "$DEST/"
echo " -> published to jniLibs/$ABI/:"; for f in "$DEST"/*.so; do echo " $(basename "$f") [$(file -b "$f" | cut -d, -f1-2)]"; done
fi