Files
shoes/scripts/build-android.sh
T
Andrey YastrebovandClaude Sonnet 4.6 80f910874b Add mobile build artifacts for iOS and Android (#127)
* Add mobile build artifacts for iOS and Android

- Add GitHub Actions workflow to build XCFramework (iOS) and AAR (Android)
  and publish them to GitHub releases on tag pushes
- Add scripts/build-ios.sh and scripts/build-android.sh for local builds
- Add android/ project (Gradle library module with Kotlin JNI wrapper)
- Add include/shoes.h C header for FFI consumers
- Gitignore /output build directory

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* Fix Android JNI build: pin jni to 0.21, use --locked

The committed Cargo.toml had `jni = "*"` which resolved to jni 0.22.4 in
CI. jni 0.22 renamed JNIEnv to EnvUnowned, breaking the Android FFI code
written for the 0.21 API.

- Pin jni to "=0.21" (resolves to 0.21.1 via Cargo.lock)
- Add --locked to cargo ndk to enforce the lock file in CI

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* Migrate Android JNI bindings to jni 0.22

jni 0.22 split JNIEnv into two types:
- EnvUnowned<'local> — FFI-safe type for native method signatures
- Env<'local> — high-level wrapper with get_string, new_string, etc.

Changes:
- Replace JNIEnv with EnvUnowned in all function signatures
- Use EnvUnowned::with_env() to access the Env inside each function
- Use Outcome enum (Ok/Err/Panic) from into_outcome() for error handling
- Replace new_global_ref return type: GlobalRef -> Global<JObject<'static>>
- Replace attach_current_thread() guard pattern with callback closure
- Replace &str method names/sigs with jni_str!/jni_sig! compile-time macros

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* Auto-generate iOS C header with cbindgen

Replace hand-written include/shoes.h with cbindgen-generated output.
The header is now derived from doc comments on extern "C" functions
in src/ffi/ios.rs, keeping it in sync with the Rust source.

- Add cbindgen.toml (exports only ProtectSocketCallback + FFI functions)
- build-ios.sh runs cbindgen before building the XCFramework
- CI installs cbindgen in the iOS job

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix clippy and rustfmt CI failures

- Reorder jni imports in ffi/android.rs to match rustfmt order
- Remove unnecessary `as usize` cast in shadow_tls_server_handler.rs
- Collapse match guard in config/validate.rs (clippy::collapsible_match)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 01:46:58 +08:00

99 lines
2.8 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Build shoes as an Android AAR.
#
# Output: output/android/shoes-release.aar
#
# Requirements:
# - Android NDK: set ANDROID_NDK_HOME, or NDK_HOME, or have ANDROID_HOME with NDK installed
# - cargo-ndk: installed automatically if missing
# - Gradle 8.6+ available as `gradle` in PATH, or ./gradlew in android/
# - Java 17+
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
ANDROID_DIR="$ROOT_DIR/android"
OUTPUT_DIR="$ROOT_DIR/output/android"
JNI_LIBS_DIR="$ANDROID_DIR/src/main/jniLibs"
cd "$ROOT_DIR"
# Resolve NDK path
if [ -z "${ANDROID_NDK_HOME:-}" ]; then
if [ -n "${NDK_HOME:-}" ]; then
export ANDROID_NDK_HOME="$NDK_HOME"
elif [ -n "${ANDROID_HOME:-}" ] && [ -d "$ANDROID_HOME/ndk" ]; then
ANDROID_NDK_HOME="$(ls -d "$ANDROID_HOME/ndk/"*/ 2>/dev/null | sort -V | tail -1)"
ANDROID_NDK_HOME="${ANDROID_NDK_HOME%/}"
export ANDROID_NDK_HOME
else
echo "Error: ANDROID_NDK_HOME is not set."
echo " Set it to the NDK directory, e.g.:"
echo " export ANDROID_NDK_HOME=\$HOME/Library/Android/sdk/ndk/26.3.11579264"
exit 1
fi
fi
echo "==> Using NDK: $ANDROID_NDK_HOME"
# Install cargo-ndk if needed
if ! command -v cargo-ndk &>/dev/null; then
echo "==> Installing cargo-ndk"
cargo install cargo-ndk
fi
echo "==> Adding Android Rust targets"
rustup target add \
aarch64-linux-android \
armv7-linux-androideabi
echo "==> Cleaning JNI libs directory"
rm -rf "$JNI_LIBS_DIR"
echo "==> Building native .so files for all ABIs"
cargo ndk \
-t arm64-v8a \
-t armeabi-v7a \
-P 21 \
-o "$JNI_LIBS_DIR" \
-- build --release --lib
echo "==> Built libraries:"
find "$JNI_LIBS_DIR" -name "*.so" | sort | sed 's|^| |'
echo "==> Building AAR with Gradle"
cd "$ANDROID_DIR"
# Ensure ANDROID_HOME is set so Gradle can find the SDK
if [ -z "${ANDROID_HOME:-}" ]; then
if [ -d "$HOME/Library/Android/sdk" ]; then
export ANDROID_HOME="$HOME/Library/Android/sdk"
elif [ -d "$HOME/Android/sdk" ]; then
export ANDROID_HOME="$HOME/Android/sdk"
fi
fi
if ! command -v gradle &>/dev/null; then
echo "Error: 'gradle' not found in PATH."
echo " Install Gradle: https://gradle.org/install/"
echo " Or run: sdk install gradle 8.6 (via SDKMAN)"
exit 1
fi
gradle assembleRelease --no-daemon
# Copy output
mkdir -p "$OUTPUT_DIR"
AAR="$(find "$ANDROID_DIR/build/outputs/aar" -name "*-release.aar" | head -1)"
if [ -z "$AAR" ]; then
echo "Error: AAR not found in android/build/outputs/aar/"
exit 1
fi
cp "$AAR" "$OUTPUT_DIR/shoes-release.aar"
echo ""
echo "Done: $OUTPUT_DIR/shoes-release.aar"
echo ""
echo "Gradle integration (app/build.gradle.kts):"
echo " implementation(files(\"libs/shoes-release.aar\"))"