diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index bcf6f60e8c8..ba4dd90cf55 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -9,6 +9,8 @@ on: paths: - .ci/docker/** - .github/workflows/android.yml + - build/build_android_library.sh + - build/test_android_ci.sh - install_requirements.sh - examples/demo-apps/android/** - extension/android/** @@ -45,6 +47,8 @@ jobs: BUILD_TOOL=${{ matrix.build-tool }} # Setup MacOS dependencies as there is no Docker support on MacOS atm PYTHON_EXECUTABLE=python bash .ci/scripts/setup-linux.sh "${BUILD_TOOL}" + # Build Android library + bash build/build_android_library.sh # Build Android demo app bash build/test_android_ci.sh @@ -61,8 +65,8 @@ jobs: cp cmake-out-android-x86_64/lib/*.a artifacts-to-be-uploaded/x86_64/ cp cmake-out-android-x86_64/extension/android/*.so artifacts-to-be-uploaded/x86_64/ # Copyp AAR to S3 - cp build_aar/executorch.aar artifacts-to-be-uploaded/ - cp build_aar/executorch-llama.aar artifacts-to-be-uploaded/ + cp executorch.aar artifacts-to-be-uploaded/ + cp executorch-llama.aar artifacts-to-be-uploaded/ # Upload the app and its test suite to S3 so that they can be downloaded by the test job upload-artifacts: diff --git a/build/build_android_library.sh b/build/build_android_library.sh new file mode 100644 index 00000000000..b224a9876de --- /dev/null +++ b/build/build_android_library.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +set -ex + +build_jar() { + pushd extension/android + ./gradlew build + popd + cp extension/android/build/libs/executorch.jar "${BUILD_AAR_DIR}/libs" +} + +build_android_native_library() { + ANDROID_ABI="$1" + ANDROID_NDK="${ANDROID_NDK:-/opt/ndk}" + CMAKE_OUT="cmake-out-android-$1" + cmake . -DCMAKE_INSTALL_PREFIX="${CMAKE_OUT}" \ + -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK}/build/cmake/android.toolchain.cmake" \ + -DANDROID_ABI="${ANDROID_ABI}" \ + -DANDROID_PLATFORM=android-23 \ + -DEXECUTORCH_BUILD_XNNPACK=ON \ + -DEXECUTORCH_BUILD_EXTENSION_DATA_LOADER=ON \ + -DEXECUTORCH_BUILD_EXTENSION_MODULE=ON \ + -DEXECUTORCH_BUILD_OPTIMIZED=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -B"${CMAKE_OUT}" + + if [ "$(uname)" == "Darwin" ]; then + CMAKE_JOBS=$(( $(sysctl -n hw.ncpu) - 1 )) + else + CMAKE_JOBS=$(( $(nproc) - 1 )) + fi + cmake --build "${CMAKE_OUT}" -j "${CMAKE_JOBS}" --target install --config Release + + cmake examples/models/llama2 \ + -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \ + -DANDROID_ABI="$ANDROID_ABI" \ + -DANDROID_PLATFORM=android-23 \ + -DCMAKE_INSTALL_PREFIX="${CMAKE_OUT}" \ + -DEXECUTORCH_BUILD_XNNPACK=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -B"${CMAKE_OUT}"/examples/models/llama2 + + cmake --build "${CMAKE_OUT}"/examples/models/llama2 -j "${CMAKE_JOBS}" --config Release + + cmake extension/android \ + -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \ + -DANDROID_ABI="${ANDROID_ABI}" \ + -DANDROID_PLATFORM=android-23 \ + -DCMAKE_INSTALL_PREFIX="${CMAKE_OUT}" \ + -DEXECUTORCH_BUILD_LLAMA_JNI=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -B"${CMAKE_OUT}"/extension/android + + cmake --build "${CMAKE_OUT}"/extension/android -j "${CMAKE_JOBS}" --config Release + + cp "${CMAKE_OUT}"/extension/android/*.so "${BUILD_AAR_DIR}/jni/$1/" +} + +build_aar() { + echo \ \ + \ \ + \ > "${BUILD_AAR_DIR}/AndroidManifest.xml" + pushd "${BUILD_AAR_DIR}" + # Rename libexecutorch_jni.so to libexecutorch.so for soname consistency + # between Java and JNI + mv jni/arm64-v8a/libexecutorch_jni.so jni/arm64-v8a/libexecutorch.so + mv jni/x86_64/libexecutorch_jni.so jni/x86_64/libexecutorch.so + zip -r executorch.aar libs jni/arm64-v8a/libexecutorch.so jni/x86_64/libexecutorch.so AndroidManifest.xml + + rm jni/arm64-v8a/libexecutorch.so jni/x86_64/libexecutorch.so + zip -r executorch-llama.aar libs jni/arm64-v8a/libexecutorch_llama_jni.so jni/x86_64/libexecutorch_llama_jni.so AndroidManifest.xml + popd + cp "${BUILD_AAR_DIR}/executorch-llama.aar" . + cp "${BUILD_AAR_DIR}/executorch.aar" . +} + +BUILD_AAR_DIR="$(mktemp -d)" +export BUILD_AAR_DIR +mkdir -p "${BUILD_AAR_DIR}/jni/arm64-v8a" "${BUILD_AAR_DIR}/jni/x86_64" "${BUILD_AAR_DIR}/libs" +build_jar +build_android_native_library arm64-v8a +build_android_native_library x86_64 +build_aar diff --git a/build/test_android_ci.sh b/build/test_android_ci.sh index 1ae26872891..9ad24e769b7 100755 --- a/build/test_android_ci.sh +++ b/build/test_android_ci.sh @@ -18,47 +18,23 @@ export_model() { cp "${MODEL_NAME}_xnnpack_fp32.pte" "${ASSETS_DIR}" } -build_android_native_library() { - pushd examples/demo-apps/android/LlamaDemo - CMAKE_OUT="cmake-out-android-$1" ANDROID_NDK=/opt/ndk ANDROID_ABI="$1" ./gradlew setup - popd - cp "cmake-out-android-$1"/extension/android/*.so build_aar/jni/$1/ -} - build_android_demo_app() { + mkdir -p examples/demo-apps/android/ExecuTorchDemo/app/libs + cp executorch.aar examples/demo-apps/android/ExecuTorchDemo/app/libs pushd examples/demo-apps/android/ExecuTorchDemo ANDROID_HOME=/opt/android/sdk ./gradlew build popd } build_android_llama_demo_app() { + mkdir -p examples/demo-apps/android/LlamaDemo/app/libs + cp executorch-llama.aar examples/demo-apps/android/LlamaDemo/app/libs pushd examples/demo-apps/android/LlamaDemo ANDROID_HOME=/opt/android/sdk ./gradlew build ANDROID_HOME=/opt/android/sdk ./gradlew assembleAndroidTest popd } -build_aar() { - cp extension/android/build/libs/executorch.jar build_aar/libs - echo \ \ - \ \ - \ > build_aar/AndroidManifest.xml - pushd build_aar - mv jni/arm64-v8a/libexecutorch_jni.so jni/arm64-v8a/libexecutorch.so - mv jni/x86_64/libexecutorch_jni.so jni/x86_64/libexecutorch.so - zip -r executorch.aar libs jni AndroidManifest.xml - - rm jni/arm64-v8a/libexecutorch.so jni/x86_64/libexecutorch.so - zip -r executorch-llama.aar libs jni AndroidManifest.xml - popd -} - -mkdir -p build_aar/jni/arm64-v8a build_aar/jni/x86_64 build_aar/libs - -build_android_native_library arm64-v8a -build_android_native_library x86_64 export_model build_android_demo_app build_android_llama_demo_app -build_aar diff --git a/docs/source/android-prebuilt-library.md b/docs/source/android-prebuilt-library.md new file mode 100644 index 00000000000..7bf6de1c23f --- /dev/null +++ b/docs/source/android-prebuilt-library.md @@ -0,0 +1,37 @@ +# Using Android prebuilt libraries (AAR) + +We provide two prebuilt Android libraries (AAR), `executorch.aar` for generic use case (image/audio processing) and `executorch_llama.aar` for LLAMA use case. + +## Contents of libraries +- `executorch.aar` + - [Java library](https://github.com/pytorch/executorch/tree/release/0.2/extension/android/src/main/java/org/pytorch/executorch) + - JNI contains the JNI binding for [NativePeer.java](https://github.com/pytorch/executorch/blob/release/0.2/extension/android/src/main/java/org/pytorch/executorch/NativePeer.java) and ExecuTorch native library, including core ExecuTorch runtime libraries, XNNPACK backend, Portable kernels, Optimized kernels, and Quantized kernels. + - Comes with two ABI variants, arm64-v8a and x86_64. +- `executorch_llama.aar` + - [Java library](https://github.com/pytorch/executorch/tree/release/0.2/extension/android/src/main/java/org/pytorch/executorch) (Note: it contains the same Java classes as the previous Java, but it does not contain the JNI binding for generic Module/NativePeer Java code). + - JNI contains the JNI binding for [LlamaModule.java](https://github.com/pytorch/executorch/blob/release/0.2/extension/android/src/main/java/org/pytorch/executorch/LlamaModule.java) and ExecuTorch native library, including core ExecuTorch runtime libraries, XNNPACK backend, Portable kernels, Optimized kernels, Quantized kernels, and LLAMA-specific Custom ops library. + - Comes with two ABI variants, arm64-v8a and x86_64. + +## Downloading AAR +[executorch.aar](https://ossci-android.s3.us-west-1.amazonaws.com/executorch/release/0.2.1/executorch.aar) (sha1sum: af7690394fd978603abeff40cf64bd2df0dc793a) +[executorch_llama.aar](https://ossci-android.s3.us-west-1.amazonaws.com/executorch/release/0.2.1/executorch-llama.aar) (sha1sum: 2973b1c41aa2c2775482d7cc7c803d0f6ca282c1) + +## Using prebuilt libraries + +To add the Java library to your app, simply download the AAR, and add it to your gradle build rule. + +In your app working directory, such as example executorch/examples/demo-apps/android/LlamaDemo, +``` +mkdir -p app/libs +curl https://ossci-android.s3.us-west-1.amazonaws.com/executorch/release/0.2.1/executorch-llama.aar -o app/libs/executorch.aar +``` + +And include it in gradle: +``` +# app/build.grardle.kts +dependencies { + implementation(files("libs/executorch-llama.aar")) +} +``` + +Now you can compile your app with the ExecuTorch Android library. diff --git a/examples/demo-apps/android/LlamaDemo/.gitignore b/examples/demo-apps/android/LlamaDemo/.gitignore index e7bee2e2b1c..41853c0472c 100644 --- a/examples/demo-apps/android/LlamaDemo/.gitignore +++ b/examples/demo-apps/android/LlamaDemo/.gitignore @@ -9,3 +9,4 @@ .cxx local.properties *.so +*.aar diff --git a/examples/demo-apps/android/LlamaDemo/README.md b/examples/demo-apps/android/LlamaDemo/README.md index 0c70ec1620a..6ea0674e7ed 100644 --- a/examples/demo-apps/android/LlamaDemo/README.md +++ b/examples/demo-apps/android/LlamaDemo/README.md @@ -27,7 +27,33 @@ adb push tokenizer.bin /data/local/tmp/llama Note: The demo app searches in `/data/local/tmp/llama` for .pte and .bin files as LLAMA model and tokenizer. -## Build JNI library +## Build library +For the demo app to build, we need to build the ExecuTorch AAR library first. + +The AAR library contains the required Java package and the corresponding JNI +library for using ExecuTorch in your Android app. + +### Alternative 1: Use prebuilt AAR library (recommended) + +1. Open a terminal window and navigate to the root directory of the `executorch`. +2. Run the following command to download the prebuilt library: +```bash +bash examples/demo-apps/android/LlamaDemo/download_prebuilt_lib.sh +``` + +The prebuilt AAR library contains the Java library and the JNI binding for +NativePeer.java and ExecuTorch native library, including core ExecuTorch +runtime libraries, XNNPACK backend, Portable kernels, Optimized kernels, +and Quantized kernels. It comes with two ABI variants, arm64-v8a and x86_64. + +If you want to use the prebuilt library for your own app, please refer to +[Using Android prebuilt libraries (AAR)](./android-prebuilt-library.md) for +tutorial. + +If you need to use other dependencies (like tokenizer), please refer to +Alternative 2: Build from local machine option. + +### Alternative 2: Build from local machine 1. Open a terminal window and navigate to the root directory of the `executorch`. 2. Set the following environment variables: ```bash diff --git a/examples/demo-apps/android/LlamaDemo/app/build.gradle.kts b/examples/demo-apps/android/LlamaDemo/app/build.gradle.kts index 5cd35a915a5..b2eb262c7de 100644 --- a/examples/demo-apps/android/LlamaDemo/app/build.gradle.kts +++ b/examples/demo-apps/android/LlamaDemo/app/build.gradle.kts @@ -56,9 +56,7 @@ dependencies { implementation("androidx.camera:camera-core:1.3.0-rc02") implementation("androidx.constraintlayout:constraintlayout:2.2.0-alpha12") implementation("com.facebook.fbjni:fbjni:0.5.1") - implementation("org.pytorch.executorch:executorch") { - exclude("com.facebook.fbjni", "fbjni-java-only") - } + implementation(files("libs/executorch-llama.aar")) testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") @@ -76,3 +74,12 @@ tasks.register("setup") { } } } + +tasks.register("download_prebuilt_lib") { + doFirst { + exec { + commandLine("sh", "examples/demo-apps/android/LlamaDemo/download_prebuilt_lib.sh") + workingDir("../../../../../") + } + } +} diff --git a/examples/demo-apps/android/LlamaDemo/download_prebuilt_lib.sh b/examples/demo-apps/android/LlamaDemo/download_prebuilt_lib.sh new file mode 100644 index 00000000000..43e2bcc14d8 --- /dev/null +++ b/examples/demo-apps/android/LlamaDemo/download_prebuilt_lib.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +set -eu + +AAR_URL="https://ossci-android.s3.us-west-1.amazonaws.com/executorch/release/0.2.1/executorch-llama.aar" +AAR_SHASUM="2973b1c41aa2c2775482d7cc7c803d0f6ca282c1" + +LIBS_PATH="$(dirname "$0")/app/libs" +AAR_PATH="${LIBS_PATH}/executorch-llama.aar" + +mkdir -p "$LIBS_PATH" + +if [[ ! -f "${AAR_PATH}" || "${AAR_SHASUM}" != $(shasum "${AAR_PATH}" | awk '{print $1}') ]]; then + curl "${AAR_URL}" -o "${AAR_PATH}" +fi diff --git a/examples/demo-apps/android/LlamaDemo/settings.gradle.kts b/examples/demo-apps/android/LlamaDemo/settings.gradle.kts index 40adb48f270..ba0e809fd98 100644 --- a/examples/demo-apps/android/LlamaDemo/settings.gradle.kts +++ b/examples/demo-apps/android/LlamaDemo/settings.gradle.kts @@ -25,5 +25,3 @@ dependencyResolutionManagement { rootProject.name = "ExecuTorch Demo" include(":app") - -includeBuild("../../../../extension/android") diff --git a/examples/demo-apps/android/LlamaDemo/setup.sh b/examples/demo-apps/android/LlamaDemo/setup.sh index f515aa22cc7..e47a9ad38ef 100644 --- a/examples/demo-apps/android/LlamaDemo/setup.sh +++ b/examples/demo-apps/android/LlamaDemo/setup.sh @@ -47,6 +47,16 @@ cmake extension/android \ cmake --build "${CMAKE_OUT}"/extension/android -j "${CMAKE_JOBS}" --config Release -JNI_LIBS_PATH="examples/demo-apps/android/LlamaDemo/app/src/main/jniLibs" -mkdir -p "${JNI_LIBS_PATH}/${ANDROID_ABI}" -cp "${CMAKE_OUT}"/extension/android/libexecutorch_llama_jni.so "${JNI_LIBS_PATH}/${ANDROID_ABI}/" +BUILD_AAR_DIR="$(mktemp -d)" +mkdir -p "${BUILD_AAR_DIR}/jni/${ANDROID_ABI}" "${BUILD_AAR_DIR}/libs" +cp "${CMAKE_OUT}"/extension/android/libexecutorch_llama_jni.so "${BUILD_AAR_DIR}/jni/${ANDROID_ABI}" +cp extension/android/build/libs/executorch.jar "${BUILD_AAR_DIR}/libs" +echo \ \ + \ \ + \ > "${BUILD_AAR_DIR}/AndroidManifest.xml" +pushd "${BUILD_AAR_DIR}" +zip -r executorch-llama.aar libs jni/${ANDROID_ABI} AndroidManifest.xml +popd +mkdir -p examples/demo-apps/android/LlamaDemo/app/libs +mv "${BUILD_AAR_DIR}/executorch-llama.aar" examples/demo-apps/android/LlamaDemo/app/libs