From 304fafa2a0ad02a5e3b33d9b4095c5da3a9f6954 Mon Sep 17 00:00:00 2001 From: Leandro Rodrigues Date: Sun, 19 Jul 2026 17:26:22 -0300 Subject: [PATCH 1/2] rust: add android support in cargo and make rust-android-test CI --- .github/workflows/rust-android-test.yml | 55 +++++++++++++++++++++++++ bindings/rust/libmem-sys/build.rs | 2 + 2 files changed, 57 insertions(+) create mode 100644 .github/workflows/rust-android-test.yml diff --git a/.github/workflows/rust-android-test.yml b/.github/workflows/rust-android-test.yml new file mode 100644 index 00000000..9755b4b4 --- /dev/null +++ b/.github/workflows/rust-android-test.yml @@ -0,0 +1,55 @@ +name: Rust - Android arm64 Link Test + +on: [workflow_dispatch] + +env: + CARGO_TERM_COLOR: always + ANDROID_NDK_VERSION: 27.0.12077973 + ANDROID_PLATFORM: android-26 + RUST_TARGET: aarch64-linux-android + +jobs: + build-and-link: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: Set up Android SDK + uses: android-actions/setup-android@v3 + + - name: Install Android toolchain + run: | + sdkmanager "platform-tools" "platforms;android-35" "ndk;${ANDROID_NDK_VERSION}" "cmake;3.22.1" + + - name: Set up Rust target + run: rustup target add "${RUST_TARGET}" + + - name: Build libmem static library for Android + run: | + cmake -S . -B build/android \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake" \ + -DANDROID_ABI=arm64-v8a \ + -DANDROID_PLATFORM="$ANDROID_PLATFORM" \ + -DANDROID_STL=c++_shared \ + -DLIBMEM_BUILD_STATIC=ON \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + cmake --build build/android --parallel + + - name: Link Rust bindings for Android + env: + LIBMEM_DIR: ${{ github.workspace }}/build/android + working-directory: bindings/rust + run: | + export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android26-clang" + cargo test --no-run --target "${RUST_TARGET}" -p libmem --locked diff --git a/bindings/rust/libmem-sys/build.rs b/bindings/rust/libmem-sys/build.rs index 1d07f2ac..f12e10bc 100644 --- a/bindings/rust/libmem-sys/build.rs +++ b/bindings/rust/libmem-sys/build.rs @@ -198,6 +198,8 @@ fn main() { vec!["user32", "psapi", "ntdll", "shell32"] } else if target_os == "linux" { vec!["dl", "m", "stdc++"] + } else if target_os == "android" { + vec!["dl", "m", "c++_shared"] } else if target_os == "freebsd" { vec!["dl", "kvm", "procstat", "elf", "m", "stdc++"] } else { From c7aa1b391548d144039ad5d56ec19c3d675b85d8 Mon Sep 17 00:00:00 2001 From: Leandro Rodrigues Date: Sun, 19 Jul 2026 19:09:27 -0300 Subject: [PATCH 2/2] rust: add android target to release.yml and fetch build Add aarch64-android-static to the release matrix (NDK/cmake build in tools/build-release.sh) so libmem-sys's fetch feature can resolve a prebuilt archive for Android. Also fix build.rs archive naming, which produced a double-dash filename on Android due to the empty target_env, breaking the fetch download URL. --- .github/workflows/release.yml | 15 ++++++++++ bindings/rust/libmem-sys/build.rs | 20 ++++++++++--- tools/build-release.sh | 50 +++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 921297c5..86c298f3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,6 +65,7 @@ jobs: {"target": "x86_64-windows-gnu-msvcrt-static", "runs-on": "ubuntu-latest"} {"target": "x86_64-windows-gnu-ucrt-shared", "runs-on": "ubuntu-latest"} {"target": "x86_64-windows-gnu-ucrt-static", "runs-on": "ubuntu-latest"} + {"target": "aarch64-android-static", "runs-on": "ubuntu-latest"} ' ) printf 'Matrix: %s\n' "$(jq <<< "$matrix")" @@ -78,12 +79,26 @@ jobs: fail-fast: false matrix: ${{ fromJson(needs.prepare.outputs.matrix) }} runs-on: ${{ matrix.runs-on }} + env: + ANDROID_NDK_VERSION: 27.0.12077973 steps: - name: Checkout uses: actions/checkout@v4 with: ref: ${{ github.event.inputs.checkout-branch }} submodules: true + - name: Set up Java + if: contains(matrix.target, '-android-') + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + - name: Set up Android SDK + if: contains(matrix.target, '-android-') + uses: android-actions/setup-android@v3 + - name: Install Android toolchain + if: contains(matrix.target, '-android-') + run: sdkmanager "platform-tools" "platforms;android-35" "ndk;${ANDROID_NDK_VERSION}" "cmake;3.22.1" - name: Build run: ./tools/build-release.sh "$BUILD_TARGETS" shell: bash diff --git a/bindings/rust/libmem-sys/build.rs b/bindings/rust/libmem-sys/build.rs index f12e10bc..a76d6f05 100644 --- a/bindings/rust/libmem-sys/build.rs +++ b/bindings/rust/libmem-sys/build.rs @@ -49,10 +49,18 @@ fn get_fetch_information() -> FetchInfo { }; // Format archive prefix - let release_target = format!( - "libmem-{}-{}-{}-{}-{}", - version, target_arch, target_os, target_env, build_type - ); + // NOTE: target_env can be empty (e.g. on Android), in which case it is omitted + let release_target = if target_env.is_empty() { + format!( + "libmem-{}-{}-{}-{}", + version, target_arch, target_os, build_type + ) + } else { + format!( + "libmem-{}-{}-{}-{}-{}", + version, target_arch, target_os, target_env, build_type + ) + }; // Format archive URL let archive_ext = "tar.gz"; @@ -159,6 +167,10 @@ fn run_tests() { "libmem-1337-x86_64-windows-gnu-ucrt-static", ["1337", "windows", "x86_64", "gnu", "llvm"], ), + ( + "libmem-1337-aarch64-android-static", + ["1337", "android", "aarch64", "", ""], + ), ]); for (expected, cargo_vars) in test_cases { diff --git a/tools/build-release.sh b/tools/build-release.sh index 95e6e0b4..8d3c53bc 100755 --- a/tools/build-release.sh +++ b/tools/build-release.sh @@ -38,6 +38,13 @@ declare -gr WINDOWS_GNU_VARIANTS=( static ) +declare -gr ANDROID_PLATFORMS=( + aarch64-android +) +declare -gr ANDROID_VARIANTS=( + static +) + SCRIPT_DIR=$(dirname -- "$(realpath -m -- "$0")") declare -gr SCRIPT_DIR PROJECT_DIR=$(dirname -- "$SCRIPT_DIR") @@ -62,6 +69,11 @@ function define_targets() { done fi done + for platform in "${ANDROID_PLATFORMS[@]}"; do + for variant in "${ANDROID_VARIANTS[@]}"; do + TARGETS+=("${platform}-${variant}") + done + done declare -gr TARGETS } @@ -133,6 +145,7 @@ function main() { case "$target" in *-linux-*) _build_in_docker "$target" "$source_dir" "$out_dir" ;; *-windows-gnu-*) _build_in_docker "$target" "$source_dir" "$out_dir" ;; + *-android-*) _build_android "$target" "$source_dir" "$out_dir" ;; *) _build_locally "$target" "$source_dir" "$out_dir" ;; esac @@ -200,6 +213,25 @@ function _build_locally() { bash <<<"set -Eeuo pipefail; shopt -s inherit_errexit; $(declare -f do_build); do_build; exit 0" } +# Builds an Android target locally using the Android NDK's CMake toolchain. +# Requires ANDROID_NDK_ROOT to point to an installed NDK (see ANDROID_NDK_VERSION +# in .github/workflows/rust-android-test.yml for the version this is tested against). +function _build_android() { + local target=$1 source_dir=$2 out_dir=$3 + + : "${ANDROID_NDK_ROOT:?ANDROID_NDK_ROOT must be set to an installed Android NDK path}" + + init_temp_dir + _=_ \ + _TARGET="$target" \ + _SOURCE_DIR="$source_dir" \ + _BUILD_DIR="${g_temp_dir}/build" \ + _OUT_DIR="$out_dir" \ + ANDROID_NDK_ROOT="$ANDROID_NDK_ROOT" \ + ANDROID_PLATFORM="${ANDROID_PLATFORM:-android-26}" \ + bash <<<"set -Eeuo pipefail; shopt -s inherit_errexit; $(declare -f do_build); do_build; exit 0" +} + # Perform the build and copy the results to the output directory. # This function must be self-contained and exportable. # Inputs: @@ -253,6 +285,24 @@ function do_build() { variant_conf+=(-DCMAKE_C_FLAGS="$flags" -DCMAKE_CXX_FLAGS="$flags") fi ;; + *-android-*) + local android_abi + case "$_TARGET" in + aarch64-*) android_abi='arm64-v8a' ;; + *) + printf 'error: Unable to determine Android ABI from target: %s\n' "$_TARGET" >&2 + return 1 + ;; + esac + variant_conf+=( + -G 'Unix Makefiles' + -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT:?ANDROID_NDK_ROOT required for android builds}/build/cmake/android.toolchain.cmake" + -DANDROID_ABI="$android_abi" + -DANDROID_PLATFORM="${ANDROID_PLATFORM:-android-26}" + -DANDROID_STL=c++_shared + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + ) + ;; *) local flags case "$_TARGET" in