xref: /aosp_15_r20/external/executorch/build/build_android_llm_demo.sh (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
1#!/bin/bash
2# Copyright (c) Meta Platforms, Inc. and affiliates.
3# All rights reserved.
4#
5# This source code is licensed under the BSD-style license found in the
6# LICENSE file in the root directory of this source tree.
7
8set -ex
9
10build_jar() {
11  pushd extension/android
12  ./gradlew build
13  popd
14  mkdir -p "${BUILD_AAR_DIR}/libs"
15  cp extension/android/build/libs/executorch.jar "${BUILD_AAR_DIR}/libs/"
16}
17
18build_android_native_library() {
19  ANDROID_ABI="$1"
20  ANDROID_NDK="${ANDROID_NDK:-/opt/ndk}"
21  CMAKE_OUT="cmake-out-android-${ANDROID_ABI}"
22  EXECUTORCH_CMAKE_BUILD_TYPE="${EXECUTORCH_CMAKE_BUILD_TYPE:-Release}"
23  QNN_SDK_ROOT="${QNN_SDK_ROOT:-}"
24  if [ -n "$QNN_SDK_ROOT" ]; then
25    EXECUTORCH_BUILD_QNN=ON
26  else
27    EXECUTORCH_BUILD_QNN=OFF
28  fi
29
30  NEURON_BUFFER_ALLOCATOR_LIB="${NEURON_BUFFER_ALLOCATOR_LIB:-}"
31  NEURON_USDK_ADAPTER_LIB="${NEURON_USDK_ADAPTER_LIB:-}"
32  if [ -n "$NEURON_BUFFER_ALLOCATOR_LIB" ]; then
33    EXECUTORCH_BUILD_NEURON=ON
34  else
35    EXECUTORCH_BUILD_NEURON=OFF
36  fi
37
38  cmake . -DCMAKE_INSTALL_PREFIX="${CMAKE_OUT}" \
39    -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK}/build/cmake/android.toolchain.cmake" \
40    -DANDROID_ABI="${ANDROID_ABI}" \
41    -DANDROID_PLATFORM=android-26 \
42    -DEXECUTORCH_ENABLE_LOGGING=ON \
43    -DEXECUTORCH_LOG_LEVEL=Info \
44    -DEXECUTORCH_BUILD_XNNPACK=ON \
45    -DEXECUTORCH_XNNPACK_SHARED_WORKSPACE=ON \
46    -DEXECUTORCH_BUILD_EXTENSION_DATA_LOADER=ON \
47    -DEXECUTORCH_BUILD_EXTENSION_MODULE=ON \
48    -DEXECUTORCH_BUILD_EXTENSION_RUNNER_UTIL=ON \
49    -DEXECUTORCH_BUILD_EXTENSION_TENSOR=ON \
50    -DEXECUTORCH_BUILD_KERNELS_OPTIMIZED=ON \
51    -DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON \
52    -DEXECUTORCH_BUILD_KERNELS_CUSTOM=ON \
53    -DEXECUTORCH_BUILD_NEURON="${EXECUTORCH_BUILD_NEURON}" \
54    -DNEURON_BUFFER_ALLOCATOR_LIB="${NEURON_BUFFER_ALLOCATOR_LIB}" \
55    -DEXECUTORCH_BUILD_QNN="${EXECUTORCH_BUILD_QNN}" \
56    -DQNN_SDK_ROOT="${QNN_SDK_ROOT}" \
57    -DCMAKE_BUILD_TYPE="${EXECUTORCH_CMAKE_BUILD_TYPE}" \
58    -B"${CMAKE_OUT}"
59
60  if [ "$(uname)" == "Darwin" ]; then
61    CMAKE_JOBS=$(( $(sysctl -n hw.ncpu) - 1 ))
62  else
63    CMAKE_JOBS=$(( $(nproc) - 1 ))
64  fi
65  cmake --build "${CMAKE_OUT}" -j "${CMAKE_JOBS}" --target install --config "${EXECUTORCH_CMAKE_BUILD_TYPE}"
66
67  cmake extension/android \
68    -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \
69    -DANDROID_ABI="${ANDROID_ABI}" \
70    -DANDROID_PLATFORM=android-26 \
71    -DCMAKE_INSTALL_PREFIX="${CMAKE_OUT}" \
72    -DEXECUTORCH_ENABLE_LOGGING=ON \
73    -DEXECUTORCH_LOG_LEVEL=Info \
74    -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH \
75    -DNEURON_BUFFER_ALLOCATOR_LIB="$NEURON_BUFFER_ALLOCATOR_LIB" \
76    -DEXECUTORCH_BUILD_KERNELS_CUSTOM=ON \
77    -DEXECUTORCH_BUILD_LLAMA_JNI=ON \
78    -DCMAKE_BUILD_TYPE="${EXECUTORCH_CMAKE_BUILD_TYPE}" \
79    -B"${CMAKE_OUT}"/extension/android
80
81  cmake --build "${CMAKE_OUT}"/extension/android -j "${CMAKE_JOBS}" --config "${EXECUTORCH_CMAKE_BUILD_TYPE}"
82
83  # Copy artifacts to ABI specific directory
84  mkdir -p "${BUILD_AAR_DIR}/jni/${ANDROID_ABI}"
85  cp "${CMAKE_OUT}"/extension/android/*.so "${BUILD_AAR_DIR}/jni/${ANDROID_ABI}/"
86
87  # Copy QNN related so library
88  if [ -n "$QNN_SDK_ROOT" ] && [ "$ANDROID_ABI" == "arm64-v8a" ]; then
89    cp "${CMAKE_OUT}"/lib/libqnn_executorch_backend.so "${BUILD_AAR_DIR}/jni/${ANDROID_ABI}/"
90    cp "${QNN_SDK_ROOT}"/lib/aarch64-android/libQnnHtp.so "${BUILD_AAR_DIR}/jni/${ANDROID_ABI}/"
91    cp "${QNN_SDK_ROOT}"/lib/aarch64-android/libQnnSystem.so "${BUILD_AAR_DIR}/jni/${ANDROID_ABI}/"
92    cp "${QNN_SDK_ROOT}"/lib/aarch64-android/libQnnHtpV69Stub.so "${BUILD_AAR_DIR}/jni/${ANDROID_ABI}/"
93    cp "${QNN_SDK_ROOT}"/lib/aarch64-android/libQnnHtpV73Stub.so "${BUILD_AAR_DIR}/jni/${ANDROID_ABI}/"
94    cp "${QNN_SDK_ROOT}"/lib/aarch64-android/libQnnHtpV75Stub.so "${BUILD_AAR_DIR}/jni/${ANDROID_ABI}/"
95    cp "${QNN_SDK_ROOT}"/lib/hexagon-v69/unsigned/libQnnHtpV69Skel.so "${BUILD_AAR_DIR}/jni/${ANDROID_ABI}/"
96    cp "${QNN_SDK_ROOT}"/lib/hexagon-v73/unsigned/libQnnHtpV73Skel.so "${BUILD_AAR_DIR}/jni/${ANDROID_ABI}/"
97    cp "${QNN_SDK_ROOT}"/lib/hexagon-v75/unsigned/libQnnHtpV75Skel.so "${BUILD_AAR_DIR}/jni/${ANDROID_ABI}/"
98  fi
99
100  # Copy MTK related so library
101  if [ -n "$NEURON_BUFFER_ALLOCATOR_LIB" ] && [ -n "$NEURON_USDK_ADAPTER_LIB" ] && [ "$ANDROID_ABI" == "arm64-v8a" ]; then
102    cp "${CMAKE_OUT}"/backends/mediatek/libneuron_backend.so ${BUILD_AAR_DIR}/jni/${ANDROID_ABI}/
103    cp "${NEURON_BUFFER_ALLOCATOR_LIB}" ${BUILD_AAR_DIR}/jni/${ANDROID_ABI}/
104    cp "${NEURON_USDK_ADAPTER_LIB}" ${BUILD_AAR_DIR}/jni/${ANDROID_ABI}/
105  fi
106}
107
108build_aar() {
109  echo \<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" \
110  package=\"org.pytorch.executorch\"\> \
111  \<uses-sdk android:minSdkVersion=\"19\" /\> \
112  \</manifest\> > "${BUILD_AAR_DIR}/AndroidManifest.xml"
113  pushd "${BUILD_AAR_DIR}"
114  # Rename libexecutorch_jni.so to libexecutorch.so for soname consistency
115  # between Java and JNI
116  find jni -type f -name "libexecutorch_jni.so" -exec bash -c 'mv "$1" "${1/_jni/}"' bash {} \;
117  if [ "$EXECUTORCH_CMAKE_BUILD_TYPE" == "Release" ]; then
118    find jni -type f -name "*.so" -exec "$ANDROID_NDK"/toolchains/llvm/prebuilt/*/bin/llvm-strip {} \;
119  fi
120  # Zip all necessary files into the AAR file
121  zip -r executorch.aar libs jni/*/libexecutorch.so jni/*/libqnn*.so jni/*/libQnn*.so jni/*/libneuron_backend.so jni/*/libneuron_buffer_allocator.so jni/*/libneuronusdk_adapter.mtk.so AndroidManifest.xml
122  popd
123}
124
125build_android_demo_apps() {
126  mkdir -p examples/demo-apps/android/LlamaDemo/app/libs
127  cp ${BUILD_AAR_DIR}/executorch.aar examples/demo-apps/android/LlamaDemo/app/libs
128  pushd examples/demo-apps/android/LlamaDemo
129  ANDROID_HOME="${ANDROID_SDK:-/opt/android/sdk}" ./gradlew build assembleAndroidTest
130  popd
131
132  mkdir -p extension/benchmark/android/benchmark/app/libs
133  cp ${BUILD_AAR_DIR}/executorch.aar extension/benchmark/android/benchmark/app/libs
134  pushd extension/benchmark/android/benchmark
135  ANDROID_HOME="${ANDROID_SDK:-/opt/android/sdk}" ./gradlew build assembleAndroidTest
136  popd
137
138  pushd extension/android_test
139  ANDROID_HOME="${ANDROID_SDK:-/opt/android/sdk}" ./gradlew testDebugUnitTest
140  ANDROID_HOME="${ANDROID_SDK:-/opt/android/sdk}" ./gradlew build assembleAndroidTest
141  popd
142}
143
144collect_artifacts_to_be_uploaded() {
145  ARTIFACTS_DIR_NAME="$1"
146  DEMO_APP_DIR="${ARTIFACTS_DIR_NAME}/llm_demo"
147  # The app directory is named using its build flavor as a suffix.
148  mkdir -p "${DEMO_APP_DIR}"
149  # Collect the app and its test suite
150  cp examples/demo-apps/android/LlamaDemo/app/build/outputs/apk/debug/*.apk "${DEMO_APP_DIR}"
151  cp examples/demo-apps/android/LlamaDemo/app/build/outputs/apk/androidTest/debug/*.apk "${DEMO_APP_DIR}"
152  # Collect all ABI specific libraries
153  for ANDROID_ABI in "${ANDROID_ABIS[@]}"; do
154    mkdir -p "${DEMO_APP_DIR}/${ANDROID_ABI}"
155    cp cmake-out-android-${ANDROID_ABI}/lib/*.a "${DEMO_APP_DIR}/${ANDROID_ABI}/"
156    cp cmake-out-android-${ANDROID_ABI}/extension/android/*.so "${DEMO_APP_DIR}/${ANDROID_ABI}/"
157  done
158  # Collect JAR and AAR
159  cp extension/android/build/libs/executorch.jar "${DEMO_APP_DIR}"
160  find "${BUILD_AAR_DIR}/" -name 'executorch*.aar' -exec cp {} "${DEMO_APP_DIR}" \;
161  # Collect MiniBench APK
162  MINIBENCH_APP_DIR="${ARTIFACTS_DIR_NAME}/minibench"
163  mkdir -p "${MINIBENCH_APP_DIR}"
164  cp extension/benchmark/android/benchmark/app/build/outputs/apk/debug/*.apk "${MINIBENCH_APP_DIR}"
165  cp extension/benchmark/android/benchmark/app/build/outputs/apk/androidTest/debug/*.apk "${MINIBENCH_APP_DIR}"
166  # Collect Java library test
167  JAVA_LIBRARY_TEST_DIR="${ARTIFACTS_DIR_NAME}/library_test_dir"
168  mkdir -p "${JAVA_LIBRARY_TEST_DIR}"
169  cp extension/android_test/build/outputs/apk/debug/*.apk "${JAVA_LIBRARY_TEST_DIR}"
170  cp extension/android_test/build/outputs/apk/androidTest/debug/*.apk "${JAVA_LIBRARY_TEST_DIR}"
171}
172
173main() {
174  BUILD_AAR_DIR="$(mktemp -d)"
175  export BUILD_AAR_DIR
176  if [ -z "$ANDROID_ABIS" ]; then
177    ANDROID_ABIS=("arm64-v8a" "x86_64")
178  fi
179  export ANDROID_ABIS
180
181  ARTIFACTS_DIR_NAME="$1"
182
183  build_jar
184  for ANDROID_ABI in "${ANDROID_ABIS[@]}"; do
185    build_android_native_library ${ANDROID_ABI}
186  done
187  build_aar
188  build_android_demo_apps
189  if [ -n "$ARTIFACTS_DIR_NAME" ]; then
190    collect_artifacts_to_be_uploaded ${ARTIFACTS_DIR_NAME}
191  fi
192}
193
194if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
195  main "$@"
196fi
197