diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 000000000..9bc85e784 --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,91 @@ +# Android cross-build — core libdb for aarch64-linux-android (bionic libc). +# +# Dedicated workflow (does NOT touch ci.yml). Cross-compiles the core C +# library with the NDK's clang against the existing root meson.build and +# asserts that a real ARM aarch64 libdb.so is produced. This is a cross-BUILD +# test: no emulator or device is used or needed. +# +# The NDK clang is just a cross-compiler; meson's HAVE_* probes are all +# compile/link checks (which succeed cross) and sizeof/alignment resolve via +# compile-only probes, so no target execution is required and no db_config.h +# hand-patching is needed. As of this workflow the core library cross-builds +# clean with zero source changes. +# +# continue-on-error keeps the job advisory (Android is not yet a release +# target), but the cross-build + `file` assertion are a real signal: a +# regression that breaks the arm64 build turns the assert step red. + +name: Android + +on: + push: + branches: [master] + pull_request: + paths: + - 'src/**' + - 'meson.build' + - 'meson_options.txt' + - 'meson/**' + - 'dist/android/**' + - '.github/workflows/android.yml' + schedule: + # Weekly (Mon 04:23 UTC): catches NDK/toolchain drift on a low cadence. + - cron: '23 4 * * 1' + workflow_dispatch: + inputs: + api: + description: 'Android API level' + default: '24' + +concurrency: + group: android-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + cross-aarch64: + name: cross-build aarch64-linux-android (API ${{ github.event.inputs.api || '24' }}) + runs-on: ubuntu-latest + continue-on-error: true # advisory tier — Android is not yet a release target + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install Meson + Ninja + run: python -m pip install --upgrade meson ninja + + - name: Set up Android NDK + id: ndk + uses: nttld/setup-ndk@v1 + with: + # r29 matches the nixpkgs ndk-bundle this path was validated against. + ndk-version: r29 + local-cache: true + + - name: Cross-build libdb.so (aarch64) + env: + ANDROID_NDK_ROOT: ${{ steps.ndk.outputs.ndk-path }} + run: | + dist/android/build_android.sh aarch64 "${{ github.event.inputs.api || 24 }}" + + - name: Assert the artifact is an ARM aarch64 shared object + run: | + out=build-android/libdb.so + file "$out" + # Real gate: fail if it is not an AArch64 ELF shared object. + file "$out" | grep -q 'ELF 64-bit LSB shared object, ARM aarch64' \ + || { echo "::error title=Android build::libdb.so is not aarch64"; exit 1; } + echo "::notice title=Android build::core libdb cross-built for aarch64-linux-android" + + - name: Upload libdb.so (arm64) + if: always() + uses: actions/upload-artifact@v4 + with: + name: libdb-android-aarch64 + path: build-android/libdb.so + if-no-files-found: ignore diff --git a/.gitignore b/.gitignore index 8e15c2ac5..3ee23ede1 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ test/sim/TESTDIR_sim_*/ # Meson/Ninja out-of-tree build dirs build-meson/ build/ +build-android/ +build-android.cross.txt # nix build output symlink /result diff --git a/dist/android/build_android.sh b/dist/android/build_android.sh new file mode 100644 index 000000000..135dc3982 --- /dev/null +++ b/dist/android/build_android.sh @@ -0,0 +1,85 @@ +#!/bin/sh +# Cross-compile the core libdb C library for Android via the NDK's clang and +# the existing root meson.build. Produces build-android/libdb.so (aarch64 by +# default). No emulator/device needed -- this is a cross-BUILD test. +# +# The NDK clang is just a cross-compiler: meson's HAVE_* probes are all +# compile/link checks (which work when cross-compiling) and sizeof/alignment +# are resolved by compile-only probes, so no target execution is required. +# +# Usage: +# ANDROID_NDK_ROOT=/path/to/ndk dist/android/build_android.sh [ABI] [API] +# +# ABI : android ABI -> aarch64 (default) | arm | x86_64 | x86 +# API : android API level -> 24 (default) +# +# ANDROID_NDK_ROOT is exported by nttld/setup-ndk and android-actions/setup-android. +# ANDROID_NDK_HOME / NDK are accepted as fallbacks. +set -eu + +ABI="${1:-aarch64}" +API="${2:-24}" + +NDK="${ANDROID_NDK_ROOT:-${ANDROID_NDK_HOME:-${NDK:-}}}" +if [ -z "$NDK" ]; then + echo "error: set ANDROID_NDK_ROOT (or ANDROID_NDK_HOME / NDK) to the NDK root" >&2 + exit 2 +fi + +# NDK layout: toolchains/llvm/prebuilt//bin/-clang +# nixpkgs' ndk-bundle nests the real NDK under libexec/android-sdk/ndk//. +if [ ! -d "$NDK/toolchains" ]; then + nested=$(find "$NDK" -maxdepth 5 -type d -path '*/toolchains/llvm/prebuilt/*/bin' 2>/dev/null | head -1) + [ -n "$nested" ] && NDK=$(dirname "$(dirname "$(dirname "$(dirname "$(dirname "$nested")")")")") +fi + +case "$(uname -s)" in + Linux) HOSTTAG=linux-x86_64 ;; + Darwin) HOSTTAG=darwin-x86_64 ;; + *) echo "error: unsupported build host $(uname -s)" >&2; exit 2 ;; +esac +BIN="$NDK/toolchains/llvm/prebuilt/$HOSTTAG/bin" + +case "$ABI" in + aarch64) TRIPLE=aarch64-linux-android; CPUFAM=aarch64; CPU=aarch64 ;; + arm) TRIPLE=armv7a-linux-androideabi; CPUFAM=arm; CPU=armv7a ;; + x86_64) TRIPLE=x86_64-linux-android; CPUFAM=x86_64; CPU=x86_64 ;; + x86) TRIPLE=i686-linux-android; CPUFAM=x86; CPU=i686 ;; + *) echo "error: unknown ABI '$ABI' (aarch64|arm|x86_64|x86)" >&2; exit 2 ;; +esac + +CC="$BIN/${TRIPLE}${API}-clang" +CXX="$BIN/${TRIPLE}${API}-clang++" +[ -x "$CC" ] || { echo "error: NDK compiler not found: $CC" >&2; exit 2; } + +ROOT=$(cd "$(dirname "$0")/../.." && pwd) +BUILDDIR="${BUILDDIR:-$ROOT/build-android}" +CROSS="$BUILDDIR.cross.txt" +mkdir -p "$BUILDDIR" + +cat > "$CROSS" </dev/null \ + || meson setup "$BUILDDIR" "$ROOT" --cross-file "$CROSS" +ninja -C "$BUILDDIR" + +"$BIN/llvm-strip" -o "$BUILDDIR/libdb.so.stripped" "$BUILDDIR/libdb.so" 2>/dev/null || true +file "$BUILDDIR/libdb.so"