From 8f7f618025f345875fa3b5f4152ffdf8171c1d2f Mon Sep 17 00:00:00 2001 From: pancake Date: Fri, 17 Jul 2026 12:02:15 +0200 Subject: [PATCH] Add android-sdk script for multiabi toolchain ##build --- sys/android-sdk.sh | 280 +++++++++++++++++++++++++++++++++++++++++++ sys/android-shell.sh | 6 +- 2 files changed, 284 insertions(+), 2 deletions(-) create mode 100755 sys/android-sdk.sh diff --git a/sys/android-sdk.sh b/sys/android-sdk.sh new file mode 100755 index 0000000000..504a90dc8b --- /dev/null +++ b/sys/android-sdk.sh @@ -0,0 +1,280 @@ +#!/bin/bash + +# Android SDK builder + +set -euo pipefail + +CALLER_DIR=$PWD +cd "$(dirname "$0")/.." +ROOT=$PWD +SCRIPT="$ROOT/sys/android-sdk.sh" +PREFIX=/usr/local +DEFAULT_ARCHS="arm+arm64+x86+x86_64" +ARCHS=$DEFAULT_ARCHS +ANDROID_API=${ANDROID_API:-28} +JOBS=${JOBS:-4} +OUTPUT="" +PLUGINS_CFG=${R2_PLUGINS_CFG:-dist/plugins-cfg/plugins.android.cfg} +if [ -z "${SOURCE_DATE_EPOCH:-}" ]; then + SOURCE_DATE_EPOCH=$(git log -1 --format=%ct 2>/dev/null || date +%s) +fi + +showHelp() { + echo "Android SDK builder" + echo + echo "Usage: $0 [options]" + echo + echo "Options:" + echo " -a, --archs ARCHS Architectures separated by + or ," + echo " (arm, arm64, x86, x86_64; default: $DEFAULT_ARCHS)" + echo " --api LEVEL Android API level (default: $ANDROID_API)" + echo " -j, --jobs JOBS Parallel build jobs (default: $JOBS)" + echo " -o, --output FILE Output zip path" + echo " -h, --help Show this help" + echo + echo "Examples:" + echo " sys/android-sdk.sh" + echo " sys/android-sdk.sh -a arm64+x86_64 --api 28" +} + +fail() { + echo "android-sdk: $*" >&2 + exit 1 +} + +buildAbi() { + local ndk_arch=$1 + local android_abi=$2 + local target=$3 + local install_dst=$4 + local make=make + + if gmake --help >/dev/null 2>&1; then + make=gmake + fi + + export CFLAGS="-fPIC -Oz -DNDEBUG" + export LDFLAGS="" + case "$ndk_arch" in + aarch64|x86_64) + export LDFLAGS="-Wl,-z,max-page-size=16384" + ;; + esac + export AR=llvm-ar + export RANLIB=llvm-ranlib + + echo "Building Android SDK libraries for $android_abi (API $ANDROID_API)" + "$make" mrproper + cp -f "$PLUGINS_CFG" plugins.cfg + ./configure --with-compiler=android --with-ostype=android \ + --target="$target" --prefix="$PREFIX" --with-libr \ + --with-bundle-prefix --without-gpl --without-sqsh \ + --with-checks-level=0 + "$make" -s -j "$JOBS" + "$make" -s install DESTDIR="$install_dst" +} + +if [ "${1:-}" = "--build-abi" ]; then + [ $# = 5 ] || fail "invalid internal build arguments" + buildAbi "$2" "$3" "$4" "$5" + exit 0 +fi + +while [ $# -gt 0 ]; do + case "$1" in + -a|--archs) + [ $# -gt 1 ] || fail "$1 requires an argument" + ARCHS=$2 + shift 2 + ;; + --api) + [ $# -gt 1 ] || fail "$1 requires an argument" + ANDROID_API=$2 + shift 2 + ;; + -j|--jobs) + [ $# -gt 1 ] || fail "$1 requires an argument" + JOBS=$2 + shift 2 + ;; + -o|--output) + [ $# -gt 1 ] || fail "$1 requires an argument" + OUTPUT=$2 + shift 2 + ;; + -h|--help) + showHelp + exit 0 + ;; + *) + fail "unknown option: $1" + ;; + esac +done + +case "$ANDROID_API" in +""|*[!0-9]*) + fail "invalid Android API level: $ANDROID_API" + ;; +esac +case "$JOBS" in +""|*[!0-9]*) + fail "invalid job count: $JOBS" + ;; +esac +[ "$JOBS" -gt 0 ] || fail "job count must be greater than zero" +command -v zip >/dev/null 2>&1 || fail "zip is required" + +VERSION=$(./configure -qV) +SDK_NAME="radare2-$VERSION-android-sdk" +if [ -z "$OUTPUT" ]; then + OUTPUT="$CALLER_DIR/$SDK_NAME.zip" +elif [ "${OUTPUT#/}" = "$OUTPUT" ]; then + OUTPUT="$CALLER_DIR/$OUTPUT" +fi + +WORKDIR=$(mktemp -d "${TMPDIR:-/tmp}/r2-android-sdk.XXXXXX") +SDK_ROOT="$WORKDIR/package/$SDK_NAME" +mkdir -p "$SDK_ROOT/include" "$SDK_ROOT/lib" + +cleanup() { + rm -rf "$WORKDIR" +} +trap cleanup EXIT HUP INT TERM + +copyLibraries() { + local source_dir=$1 + local dest_dir=$2 + local source_file + local dest_file + + mkdir -p "$dest_dir" + while IFS= read -r source_file; do + dest_file="$dest_dir/$(basename "$source_file")" + if [ -f "$dest_file" ] && ! cmp -s "$source_file" "$dest_file"; then + fail "library name collision: $(basename "$source_file")" + fi + cp -Lf "$source_file" "$dest_file" + done < <(find "$source_dir" \( -type f -o -type l \) \( -name '*.a' -o -name '*.so' \) -print) +} + +rewritePkgConfig() { + local pkg_dir=$1 + local android_abi=$2 + local pc + + [ -d "$pkg_dir" ] || return 0 + for pc in "$pkg_dir"/*.pc; do + [ -f "$pc" ] || continue + sed \ + -e 's|^prefix=.*|prefix=${pcfiledir}/../../..|' \ + -e 's|^exec_prefix=.*|exec_prefix=${prefix}|' \ + -e "s|^libdir=.*|libdir=\${prefix}/lib/$android_abi|" \ + -e 's|^includedir=.*|includedir=${prefix}/include|' \ + "$pc" > "$pc.tmp" + mv -f "$pc.tmp" "$pc" + done +} + +FIRST_ABI=1 +BUILT_ABIS="" +NORMALIZED_ARCHS=$(printf '%s' "$ARCHS" | tr ',+' ' ') +export ANDROID_API JOBS PLUGINS_CFG SOURCE_DATE_EPOCH + +for arch in $NORMALIZED_ARCHS; do + case "$arch" in + arm|armeabi-v7a) + NDK_ARCH=arm + ANDROID_ABI=armeabi-v7a + TARGET=arm-linux-androideabi + ;; + arm64|aarch64|arm64-v8a) + NDK_ARCH=aarch64 + ANDROID_ABI=arm64-v8a + TARGET=aarch64-linux-android + ;; + x86) + NDK_ARCH=x86 + ANDROID_ABI=x86 + TARGET=i686-linux-android + ;; + x64|x86_64) + NDK_ARCH=x86_64 + ANDROID_ABI=x86_64 + TARGET=x86_64-linux-android + ;; + *) + fail "unsupported architecture: $arch" + ;; + esac + + case " $BUILT_ABIS " in + *" $ANDROID_ABI "*) + continue + ;; + esac + BUILT_ABIS="$BUILT_ABIS $ANDROID_ABI" + INSTALL_DST="$WORKDIR/install-$ANDROID_ABI" + mkdir -p "$INSTALL_DST" + sys/android-shell.sh "$NDK_ARCH" "$SCRIPT" --build-abi \ + "$NDK_ARCH" "$ANDROID_ABI" "$TARGET" "$INSTALL_DST" + INSTALL_PREFIX="$INSTALL_DST$PREFIX" + [ -d "$INSTALL_PREFIX/include/libr" ] || fail "missing headers for $ANDROID_ABI" + [ -d "$INSTALL_PREFIX/lib" ] || fail "missing libraries for $ANDROID_ABI" + + if [ "$FIRST_ABI" = 1 ]; then + cp -R "$INSTALL_PREFIX/include/." "$SDK_ROOT/include" + if [ -d "$INSTALL_PREFIX/share/radare2" ]; then + mkdir -p "$SDK_ROOT/share" + cp -R "$INSTALL_PREFIX/share/radare2" "$SDK_ROOT/share/radare2" + fi + FIRST_ABI=0 + elif ! diff -qr "$SDK_ROOT/include" "$INSTALL_PREFIX/include" >/dev/null; then + diff -ru "$SDK_ROOT/include" "$INSTALL_PREFIX/include" || true + fail "installed headers differ for $ANDROID_ABI" + fi + + ABI_LIBDIR="$SDK_ROOT/lib/$ANDROID_ABI" + copyLibraries "$INSTALL_PREFIX/lib" "$ABI_LIBDIR" + if [ -d "$INSTALL_PREFIX/lib/pkgconfig" ]; then + mkdir -p "$ABI_LIBDIR/pkgconfig" + cp -R "$INSTALL_PREFIX/lib/pkgconfig/." "$ABI_LIBDIR/pkgconfig" + rewritePkgConfig "$ABI_LIBDIR/pkgconfig" "$ANDROID_ABI" + fi + find "$ABI_LIBDIR" -maxdepth 1 -name '*.a' -print -quit | grep -q . || \ + fail "no static libraries found for $ANDROID_ABI" + find "$ABI_LIBDIR" -maxdepth 1 -name '*.so' -print -quit | grep -q . || \ + fail "no shared libraries found for $ANDROID_ABI" +done + +[ "$FIRST_ABI" = 0 ] || fail "no architectures selected" +cp -f COPYING.md "$SDK_ROOT" +printf '%s\n' \ + "radare2 Android SDK $VERSION" \ + "" \ + "Android API level: $ANDROID_API" \ + "ABIs:$BUILT_ABIS" \ + "" \ + "Layout:" \ + " include/libr/ Public radare2 headers" \ + " lib// Static and shared libraries" \ + " lib//pkgconfig/ Relocatable pkg-config files" \ + " share/ Runtime data files" \ + "" \ + "Use include/libr as an include directory and lib/\${ANDROID_ABI} as" \ + "a library directory. Set PKG_CONFIG_PATH to the matching pkgconfig" \ + "directory when using pkg-config." > "$SDK_ROOT/README.txt" +printf 'version=%s\napi=%s\nabis=%s\n' \ + "$VERSION" "$ANDROID_API" "${BUILT_ABIS# }" > "$SDK_ROOT/METADATA" + +mkdir -p "$(dirname "$OUTPUT")" +ZIP_PATH="$WORKDIR/$SDK_NAME.zip" +( + cd "$WORKDIR/package" + zip -qry "$ZIP_PATH" "$SDK_NAME" +) +if command -v unzip >/dev/null 2>&1; then + unzip -tq "$ZIP_PATH" >/dev/null +fi +mv -f "$ZIP_PATH" "$OUTPUT" +echo "$OUTPUT" diff --git a/sys/android-shell.sh b/sys/android-shell.sh index 252d355374..100a2eae87 100755 --- a/sys/android-shell.sh +++ b/sys/android-shell.sh @@ -104,15 +104,17 @@ fi echo ROOT=$ROOT echo NDK="$NDK" echo NDK_ARCH=$NDK_ARCH +[ -z "${ANDROID_API}" ] && ANDROID_API=28 +echo ANDROID_API=$ANDROID_API -TOOLCHAIN_ID="${NDK}:${ARCH}" +TOOLCHAIN_ID="${NDK}:${ARCH}:${ANDROID_API}" TOOLCHAIN_ID_FILE=/tmp/ndk/.r2-toolchain CURRENT_TOOLCHAIN_ID=`cat "${TOOLCHAIN_ID_FILE}" 2>/dev/null` if [ -x /tmp/ndk/bin/ndk-gcc ] && [ "${CURRENT_TOOLCHAIN_ID}" = "${TOOLCHAIN_ID}" ]; then echo "NDK toolchain already initialized." else echo "Building the standalone NDK toolchain..." - ${NDK}/build/tools/make_standalone_toolchain.py --arch=${ARCH} --install-dir=/tmp/ndk/ --api=28 --force || exit 1 + ${NDK}/build/tools/make_standalone_toolchain.py --arch=${ARCH} --install-dir=/tmp/ndk/ --api=${ANDROID_API} --force || exit 1 ( cd /tmp/ndk/bin/ && \ ln -fs clang ndk-gcc && \