From d09bb3764720583b9e6bd039b57bdd4a36f6d080 Mon Sep 17 00:00:00 2001 From: Greg Burd Date: Fri, 31 Jul 2026 08:36:31 -0400 Subject: [PATCH 1/2] feat(android): cross-compile core libdb for aarch64-linux-android Add dist/android/build_android.sh: derives the NDK clang paths from ANDROID_NDK_ROOT (as exported by setup-ndk / setup-android, with ANDROID_NDK_HOME / NDK fallbacks and nixpkgs ndk-bundle nesting handled), writes a meson cross-file, and drives the existing root meson.build to produce build-android/libdb.so. The NDK clang is just a cross-compiler: meson's HAVE_* probes are all compile/link checks (which succeed when cross-compiling) and sizeof/ alignment resolve via compile-only probes, so no target execution and no db_config.h hand-patching are required. The core C library cross-builds clean against bionic with zero source changes. Validated locally with nixpkgs ndk-bundle (NDK r29, API 24): file build-android/libdb.so -> ELF 64-bit LSB shared object, ARM aarch64, ... for Android 24 Also ignore the default build-android/ output dir. --- .gitignore | 2 + dist/android/build_android.sh | 85 +++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 dist/android/build_android.sh 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" From 689710aee725a4cba3cc7a82858653ddd2d0c7cd Mon Sep 17 00:00:00 2001 From: Greg Burd Date: Fri, 31 Jul 2026 08:36:31 -0400 Subject: [PATCH 2/2] ci(android): gate the aarch64-linux-android cross-build Add .github/workflows/android.yml (schedule + workflow_dispatch + PRs touching src/**, meson.build, dist/android/**). Sets up the NDK via nttld/setup-ndk@v1 (r29), runs dist/android/build_android.sh, and asserts the artifact is an 'ELF 64-bit LSB shared object, ARM aarch64' -- the assert step is a real signal even though the job is continue-on-error (advisory tier; Android is not yet a release target). Uploads libdb.so as an artifact. Modeled on fuzz.yml / ci.yml structure and comment style. --- .github/workflows/android.yml | 91 +++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 .github/workflows/android.yml 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