1# How to use the Android NDK to build Arm NN 2 3- [Introduction](#introduction) 4- [Initial Setup](#initial-setup) 5- [Download the Android NDK and make a standalone toolchain](#download-the-android-ndk-and-make-a-standalone-toolchain) 6- [Install Cmake](#install-cmake) 7- [Build Flatbuffers](#build-flatbuffers) 8- [Download Arm NN](#download-arm-nn) 9- [Get And Build TFLite](#get-and-build-tflite) 10- [Build Arm Compute Library](#build-arm-compute-library) 11- [Build Arm NN](#build-arm-nn) 12- [Build Standalone Sample Dynamic Backend](#build-standalone-sample-dynamic-backend) 13- [Run the Arm NN unit tests on an Android device](#run-the-arm-nn-unit-tests-on-an-android-device) 14 15 16## Introduction 17These are step-by-step instructions for using the Android NDK to build Arm NN. 18They have been tested on a clean installation of Ubuntu 18.04 and 20.04, and should also work with other OS versions. 19The instructions show how to build the Arm NN core library and its dependencies. 20 21For ease of use there is a shell script version of this guide located in the scripts directory called [build_android_ndk_guide.sh](scripts/build_android_ndk_guide.sh). 22Run the script with a -h flag to see the command line parameters. 23 24## Initial Setup 25 26First, we need to specify the Android version and the directories you want to build armnn in and to install some applications required to build Arm NN and its dependencies. 27 28```bash 29export ANDROID_API=30 30export WORKING_DIR=$HOME/armnn-devenv 31export NDK_DIR=$WORKING_DIR/android-ndk-r25 32export NDK_TOOLCHAIN_ROOT=$NDK_DIR/toolchains/llvm/prebuilt/linux-x86_64 33export PATH=$NDK_TOOLCHAIN_ROOT/bin/:$PATH 34``` 35You may want to append the above export variables commands to your `~/.bashrc` (or `~/.bash_profile` in macOS). 36 37The ANDROID_API variable should be set to the Android API version number you are using. For example, "30" for Android R. The WORKING_DIR can be any directory you have write permissions to. 38 39### Required Applications 40Git is required to obtain Arm NN. If this has not been already installed then install it using: 41```bash 42sudo apt install git 43``` 44 45Arm Compute Library requires SCons. If this has not been already installed then install it using: 46```bash 47sudo apt install scons 48``` 49 50CMake is required to build Arm NN and its dependencies. If this has not been already installed then install it using: 51```bash 52sudo apt install cmake 53``` 54 55## Download the Android NDK and make a standalone toolchain 56 57Download the Android NDK from [the official website](https://developer.android.com/ndk/downloads/index.html): 58```bash 59mkdir -p $WORKING_DIR 60cd $WORKING_DIR 61# For Mac OS, change the NDK download link accordingly. 62wget https://dl.google.com/android/repository/android-ndk-r25-linux.zip 63unzip android-ndk-r25-linux.zip 64``` 65With Android NDK-25, you no longer need to use the make_standalone_toolchain script to create a toolchain for a specific version of Android. Android's current preference is for you to just specify the architecture and operating system while setting the compiler and just use the ndk directory. 66 67## Install Cmake 68Cmake 3.19rc3 or later is required to build Arm NN. If you are using Ubuntu 20.04 the command given in [Initial Setup](#initial-setup) should install a usable version. If you're using Ubuntu 18.04 you may need to compile cmake yourself. 69 70```bash 71cd $WORKING_DIR 72sudo apt-get install libssl-dev 73wget https://github.com/Kitware/CMake/releases/download/v3.19.0-rc3/cmake-3.19.0-rc3.tar.gz 74tar -zxvf cmake-3.19.0-rc3.tar.gz 75cd cmake-3.19.0-rc3 76./bootstrap --prefix=$WORKING_DIR/cmake/install 77make all install 78cd.. 79``` 80 81## Build Flatbuffers 82 83Download Flatbuffers: 84```bash 85cd $WORKING_DIR 86wget https://github.com/google/flatbuffers/archive/v2.0.6.tar.gz 87tar xf v2.0.6.tar.gz 88``` 89 90Build Flatbuffers for x86: 91```bash 92cd $WORKING_DIR/flatbuffers-2.0.6 93 94rm -f CMakeCache.txt 95 96rm -rf build-x86 97mkdir build-x86 98cd build-x86 99 100rm -rf $WORKING_DIR/flatbuffers-x86 101mkdir $WORKING_DIR/flatbuffers-x86 102 103CXXFLAGS="-fPIC" $CMAKE .. \ 104 -DFLATBUFFERS_BUILD_FLATC=1 \ 105 -DCMAKE_INSTALL_PREFIX:PATH=$WORKING_DIR/flatbuffers-x86 106 107make all install -j16 108``` 109Note: -fPIC is added to allow users to use the libraries in shared objects. 110 111Build Flatbuffers for Android: 112```bash 113cd $WORKING_DIR/flatbuffers-2.0.6 114 115rm -f CMakeCache.txt 116 117rm -rf build-android 118mkdir build-android 119cd build-android 120 121rm -rf $WORKING_DIR/flatbuffers-android 122mkdir $WORKING_DIR/flatbuffers-android 123 124CC=/usr/bin/aarch64-linux-gnu-gcc CXX=/usr/bin/aarch64-linux-gnu-g++ \ 125CXXFLAGS="-fPIC" \ 126cmake .. \ 127 -DCMAKE_ANDROID_NDK=$NDK_DIR \ 128 -DCMAKE_SYSTEM_NAME=Android \ 129 -DCMAKE_SYSTEM_VERSION=27 \ 130 -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a \ 131 -DCMAKE_CXX_FLAGS=--std=c++14 \ 132 -DFLATBUFFERS_BUILD_FLATC=OFF \ 133 -DCMAKE_BUILD_TYPE=Release \ 134 -DFLATBUFFERS_BUILD_TESTS=OFF \ 135 -DCMAKE_INSTALL_PREFIX=$WORKING_DIR/flatbuffers-android 136 137make all install -j16 138``` 139 140## Download Arm NN 141Clone Arm NN: 142 143```bash 144cd $WORKING_DIR 145git clone https://github.com/ARM-software/armnn.git 146``` 147 148Checkout the Arm NN branch: 149```bash 150cd armnn 151git checkout <branch_name> 152git pull 153``` 154 155For example, if you want to check out the 23.02 release branch: 156```bash 157cd armnn 158git checkout branches/armnn_23_02 159git pull 160``` 161 162## Get And Build TFLite 163This optional step is only required if you intend to build the TFLite delegate or parser for Arm NN. 164 165First clone tensorflow: 166```bash 167cd $WORKING_DIR 168git clone https://github.com/tensorflow/tensorflow.git 169cd tensorflow 170git fetch && git checkout "tags/v2.10.0" 171``` 172Arm NN provides a script that downloads the version of Tensorflow that Arm NN was tested with: 173```bash 174git fetch && git checkout $(../armnn/scripts/get_tensorflow.sh -p) 175``` 176Next build Tensorflow Lite: 177```bash 178cd $WORKING_DIR 179mkdir -p tflite-out/android 180cd tflite-out/android 181 182CMARGS="-DCMAKE_TOOLCHAIN_FILE=$NDK_DIR/build/cmake/android.toolchain.cmake \ 183 -DANDROID_ABI=arm64-v8a \ 184 -DANDROID_PLATFORM=$ANDROID_API" 185 186cmake $CMARGS $WORKING_DIR/tensorflow/tensorflow/lite 187 188cd $WORKING_DIR 189cmake --build tflite-out/android -j 16 190``` 191Now generate the Tensorflow Lite Schema for the TFLite parser: 192```bash 193cd $WORKING_DIR 194mkdir -p $WORKING_DIR/tflite-out/tensorflow/tensorflow/lite/schema 195 196SCHEMA_LOCATION=$WORKING_DIR/tensorflow/tensorflow/lite/schema/schema.fbs 197cp $SCHEMA_LOCATION $WORKING_DIR/tflite-out/tensorflow/tensorflow/lite/schema 198 199cd $WORKING_DIR/tflite-out/tensorflow/tensorflow/lite/schema 200$WORKING_DIR/flatbuffers-x86/bin/flatc -c --gen-object-api --reflect-types --reflect-names schema.fbs 201``` 202 203## Build Arm Compute Library 204Clone Arm Compute Library: 205 206```bash 207cd $WORKING_DIR 208git clone https://github.com/ARM-software/ComputeLibrary.git 209``` 210Checkout Arm Compute Library release tag: 211```bash 212cd ComputeLibrary 213git checkout <tag_name> 214``` 215For example, if you want to check out the 23.02 release tag: 216```bash 217cd ComputeLibrary 218git checkout v23.02 219``` 220 221Arm NN and Arm Compute Library are developed closely together. To use a particular version of Arm NN you will need a compatible version of ACL. 222Arm NN provides a script that downloads the version of Arm Compute Library that Arm NN was tested with: 223```bash 224git checkout $(../armnn/scripts/get_compute_library.sh -p) 225``` 226Build the Arm Compute Library: 227```bash 228scons arch=arm64-v8a os=android toolchain_prefix=llvm- compiler_prefix=aarch64-linux-android$ANDROID_API- \ 229 neon=1 opencl=1 embed_kernels=1 extra_cxx_flags="-fPIC" \ 230 benchmark_tests=0 validation_tests=0 -j16 231``` 232 233## Build Arm NN 234 235Build Arm NN: 236 237```bash 238mkdir $WORKING_DIR/armnn/build 239cd $WORKING_DIR/armnn/build 240CXX=aarch64-linux-android$ANDROID_API-clang++ \ 241CC=aarch64-linux-android$ANDROID_API-clang \ 242CXX_FLAGS="-fPIE -fPIC" \ 243cmake .. \ 244 -DCMAKE_BUILD_TYPE=Release \ 245 -DCMAKE_ANDROID_NDK=$NDK_DIR \ 246 -DNDK_VERSION=r25 \ 247 -DCMAKE_SYSTEM_NAME=Android \ 248 -DCMAKE_SYSTEM_VERSION=$ANDROID_API \ 249 -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a \ 250 -DCMAKE_SYSROOT=$WORKING_DIR/android-ndk-r25/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ 251 -DARMCOMPUTE_ROOT=$WORKING_DIR/ComputeLibrary \ 252 -DARMCOMPUTE_BUILD_DIR=$WORKING_DIR/ComputeLibrary/build \ 253 -DARMCOMPUTENEON=1 -DARMCOMPUTECL=1 -DARMNNREF=1 \ 254 -DFLATBUFFERS_INCLUDE_PATH=$WORKING_DIR/flatbuffers-x86/include \ 255 -DFLATBUFFERS_ROOT=$WORKING_DIR/flatbuffers-android \ 256 -DFLATC_DIR=$WORKING_DIR/flatbuffers-x86 \ 257 -DBUILD_UNIT_TESTS=1 \ 258 -DBUILD_TESTS=1 \ 259 -fexception \ 260``` 261 262To include the Arm NN TFLite delegate add these arguments to the above list: 263 264```bash 265 -DBUILD_ARMNN_TFLITE_DELEGATE=1 \ 266 -DTENSORFLOW_ROOT=$WORKING_DIR/tensorflow \ 267 -DTFLITE_LIB_ROOT=$WORKING_DIR/tflite-out/android \ 268 -DTFLITE_ROOT_DIR=$WORKING_DIR/tensorflow/tensorflow/lite \ 269``` 270 271To include the Arm NN TFLite Parser add these arguments to the above list: 272 273```bash 274 -DBUILD_TF_LITE_PARSER=1 \ 275 -DTF_LITE_GENERATED_PATH=$WORKING_DIR/tflite-out/tensorflow/tensorflow/lite/schema \ 276 -DTENSORFLOW_ROOT=$WORKING_DIR/tensorflow \ 277 -DTFLITE_LIB_ROOT=$WORKING_DIR/tflite-out/android \ 278``` 279 280To include standalone sample dynamic backend tests, add these arguments to enable the tests and the dynamic backend path to the CMake command: 281 282```bash 283 -DSAMPLE_DYNAMIC_BACKEND=1 \ 284 -DDYNAMIC_BACKEND_PATHS=$SAMPLE_DYNAMIC_BACKEND_PATH 285# Where $SAMPLE_DYNAMIC_BACKEND_PATH is the path where libArm_SampleDynamic_backend.so library file is pushed 286``` 287 288 * Run the build 289```bash 290make -j16 291``` 292 293## Build Standalone Sample Dynamic Backend 294This step is optional. The sample dynamic backend is located in armnn/src/dynamic/sample 295```bash 296mkdir build 297cd build 298``` 299 300* Use CMake to configure the build environment, update the following script and run it from the armnn/src/dynamic/sample/build directory to set up the Arm NN build: 301```bash 302#!/bin/bash 303CXX=aarch64-linux-android$ANDROID_API-clang++ \ 304CC=aarch64-linux-android$ANDROID_API-clang \ 305CXX_FLAGS="-fPIE -fPIC" \ 306cmake \ 307 -DCMAKE_C_COMPILER_WORKS=TRUE \ 308 -DCMAKE_CXX_COMPILER_WORKS=TRUE \ 309 -DCMAKE_ANDROID_NDK=$NDK_DIR \ 310 -DCMAKE_SYSTEM_NAME=Android \ 311 -DCMAKE_SYSTEM_VERSION=$ANDROID_API \ 312 -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a \ 313 -DCMAKE_SYSROOT=$WORKING_DIR/android-ndk-r25/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ 314 -DCMAKE_CXX_FLAGS=--std=c++14 \ 315 -DCMAKE_EXE_LINKER_FLAGS="-pie -llog" \ 316 -DCMAKE_MODULE_LINKER_FLAGS="-llog" \ 317 -DARMNN_PATH=$WORKING_DIR/armnn/build/libarmnn.so .. 318``` 319 320* Run the build 321```bash 322make 323``` 324 325## Run the Arm NN unit tests on an Android device 326 327 328* Push the build results to an Android device and make symbolic links for shared libraries: 329 Currently adb version we have used for testing is 1.0.41. 330```bash 331adb push libarmnn.so /data/local/tmp/ 332 adb push libtimelineDecoder.so /data/local/tmp/ 333adb push UnitTests /data/local/tmp/ 334adb push $NDK_DIR/sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libc++_shared.so /data/local/tmp/ 335``` 336 337* Push the files needed for the unit tests (they are a mix of files, directories and symbolic links): 338```bash 339adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/testSharedObject 340adb push -p $WORKING_DIR/armnn/build/src/backends/backendsCommon/test/testSharedObject/* /data/local/tmp/src/backends/backendsCommon/test/testSharedObject/ 341 342adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/testDynamicBackend 343adb push -p $WORKING_DIR/armnn/build/src/backends/backendsCommon/test/testDynamicBackend/* /data/local/tmp/src/backends/backendsCommon/test/testDynamicBackend/ 344 345adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath1 346adb push -p $WORKING_DIR/armnn/build/src/backends/backendsCommon/test/backendsTestPath1/* /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath1/ 347 348adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath2 349adb push -p $WORKING_DIR/armnn/build/src/backends/backendsCommon/test/backendsTestPath2/Arm_CpuAcc_backend.so /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath2/ 350adb shell ln -s Arm_CpuAcc_backend.so /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath2/Arm_CpuAcc_backend.so.1 351adb shell ln -s Arm_CpuAcc_backend.so.1 /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath2/Arm_CpuAcc_backend.so.1.2 352adb shell ln -s Arm_CpuAcc_backend.so.1.2 /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath2/Arm_CpuAcc_backend.so.1.2.3 353adb push -p $WORKING_DIR/armnn/build/src/backends/backendsCommon/test/backendsTestPath2/Arm_GpuAcc_backend.so /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath2/ 354adb shell ln -s nothing /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath2/Arm_no_backend.so 355 356adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath3 357 358adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath5 359adb push -p $WORKING_DIR/armnn/build/src/backends/backendsCommon/test/backendsTestPath5/* /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath5/ 360 361adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath6 362adb push -p $WORKING_DIR/armnn/build/src/backends/backendsCommon/test/backendsTestPath6/* /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath6/ 363 364adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath7 365 366adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath9 367adb push -p $WORKING_DIR/armnn/build/src/backends/backendsCommon/test/backendsTestPath9/* /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath9/ 368 369adb shell mkdir -p /data/local/tmp/src/backends/dynamic/reference 370adb push -p $WORKING_DIR/armnn/build/src/backends/dynamic/reference/Arm_CpuRef_backend.so /data/local/tmp/src/backends/dynamic/reference/ 371``` 372 373If the standalone sample dynamic tests are enabled, also push libArm_SampleDynamic_backend.so library file to the folder specified as $SAMPLE_DYNAMIC_BACKEND_PATH when Arm NN is built. 374This is the example when $SAMPLE_DYNAMIC_BACKEND_PATH is specified as /data/local/tmp/dynamic/sample/: 375 376```bash 377adb shell mkdir -p /data/local/tmp/dynamic/sample/ 378adb push -p $WORKING_DIR/armnn/src/dynamic/sample/build/libArm_SampleDynamic_backend.so /data/local/tmp/dynamic/sample/ 379``` 380If the delegate was built, push the delegate unit tests too. 381```bash 382adb push $WORKING_DIR/armnn/build/delegate/DelegateUnitTests /data/local/tmp/ 383adb push $WORKING_DIR/armnn/build/delegate/libarmnnDelegate.so /data/local/tmp/ 384``` 385Run Arm NN unit tests: 386```bash 387adb shell 'LD_LIBRARY_PATH=/data/local/tmp:/vendor/lib64:/vendor/lib64/egl /data/local/tmp/UnitTests' 388``` 389If the delegate was built run Arm Delegate NN unit tests: 390```bash 391adb shell 'LD_LIBRARY_PATH=/data/local/tmp:/vendor/lib64:/vendor/lib64/egl /data/local/tmp/DelegateUnitTests' 392``` 393If libarmnnUtils.a is present in `$WORKING_DIR/armnn/build/` and the unit tests run without failure then the build was successful. 394