1# How to use the Android NDK to build ArmNN 2 3* [Introduction](#introduction) 4* [Download the Android NDK and make a standalone toolchain](#downloadNDK) 5* [Build the Boost C++ libraries](#buildBoost) 6* [Build the Compute Library](#buildCL) 7* [Build Google's Protobuf library](#buildProtobuf) 8* [Download TensorFlow](#downloadTF) 9* [Build ArmNN](#buildArmNN) 10* [Run ArmNN UnitTests on an Android device](#runArmNNUnitTests) 11 12 13#### <a name="introduction">Introduction</a> 14These are step by step instructions for using the Android NDK to build ArmNN. 15They have been tested on a clean install of Ubuntu 18.04, and should also work with other OS versions. 16The instructions show how to build the ArmNN core library and the optional TensorFlow parser. 17All downloaded or generated files will be saved inside the `~/armnn-devenv` directory. 18 19#### <a name="downloadNDK">Download the Android NDK and make a standalone toolchain</a> 20 21* Download the Android NDK from [the official website](https://developer.android.com/ndk/downloads/index.html): 22 23 ```bash 24 mkdir -p ~/armnn-devenv/toolchains 25 cd ~/armnn-devenv/toolchains 26 # For Mac OS, change the NDK download link accordingly. 27 wget https://dl.google.com/android/repository/android-ndk-r20b-linux-x86_64.zip 28 unzip android-ndk-r20b-linux-x86_64.zip 29 export NDK=~/armnn-devenv/android-ndk-r20b 30 export NDK_TOOLCHAIN_ROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64 31 export PATH=$NDK_TOOLCHAIN_ROOT/bin/:$PATH 32 ``` 33 34 You may want to append the above export variables commands to your `~/.bashrc` (or `~/.bash_profile` in Mac OS). 35 36* With the android ndk-20b, you don't 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. 37 38#### <a name="buildBoost">Build the Boost C++ libraries</a> 39 40* Download Boost version 1.64: 41 42 ```bash 43 mkdir ~/armnn-devenv/boost 44 cd ~/armnn-devenv/boost 45 wget https://dl.bintray.com/boostorg/release/1.64.0/source/boost_1_64_0.tar.bz2 46 tar xvf boost_1_64_0.tar.bz2 47 ``` 48 49* Build: 50 51 (Requires clang if not previously installed: `sudo apt-get install clang`) 52 ```bash 53 echo "using clang : arm : aarch64-linux-android<Android_API>-clang++ ;" > $HOME/armnn-devenv/boost/user-config.jam 54 cd ~/armnn-devenv/boost/boost_1_64_0 55 ./bootstrap.sh --prefix=$HOME/armnn-devenv/boost/install 56 ./b2 install --user-config=$HOME/armnn-devenv/boost/user-config.jam \ 57 toolset=clang-arm link=static cxxflags=-fPIC \ 58 --with-test --with-log --with-program_options -j16 59 ``` 60 Note: You can specify the 'Android_API' version you want. For example, if your ANDROID_API is 27 then the compiler will be aarch64-linux-android27-clang++. 61 62#### <a name="buildCL">Build the Compute Library</a> 63* Clone the Compute Library: 64 65 (Requires Git if not previously installed: `sudo apt install git`) 66 67 ``` bash 68 cd ~/armnn-devenv 69 git clone https://github.com/ARM-software/ComputeLibrary.git 70 ``` 71 72* Checkout ComputeLibrary branch: 73 ```bash 74 cd ComputeLibrary 75 git checkout <branch_name> 76 git pull 77 ``` 78 For example, if you want to checkout release branch of 20.02: 79 ```bash 80 git checkout branches/arm_compute_20_02 81 git pull 82 ``` 83 84* Build: 85 86 (Requires SCons if not previously installed: `sudo apt install scons`) 87 ```bash 88 scons arch=arm64-v8a neon=1 opencl=1 embed_kernels=1 extra_cxx_flags="-fPIC" \ 89 benchmark_tests=0 validation_tests=0 os=android -j16 90 ``` 91 92#### <a name="buildProtobuf">Build Google's Protobuf library</a> 93 94* Clone protobuf: 95 ```bash 96 mkdir ~/armnn-devenv/google 97 cd ~/armnn-devenv/google 98 git clone https://github.com/google/protobuf.git 99 cd protobuf 100 git checkout -b v3.12.0 v3.12.0 101 ``` 102 103* Build a native (x86) version of the protobuf libraries and compiler (protoc): 104 105 (Requires cUrl, autoconf, llibtool, and other build dependencies if not previously installed: `sudo apt install curl autoconf libtool build-essential g++`) 106 107 ```bash 108 ./autogen.sh 109 mkdir x86_build 110 cd x86_build 111 ../configure --prefix=$HOME/armnn-devenv/google/x86_pb_install 112 make install -j16 113 cd .. 114 ``` 115 116* Build the arm64 version of the protobuf libraries: 117 118 ```bash 119 mkdir arm64_build 120 cd arm64_build 121 CC=aarch64-linux-android<Android_API>-clang \ 122 CXX=aarch64-linux-android<Android_API>-clang++ \ 123 CFLAGS="-fPIE -fPIC" \ 124 LDFLAGS="-llog -lz -lc++_static" \ 125 ../configure --host=aarch64-linux-android \ 126 --prefix=$HOME/armnn-devenv/google/arm64_pb_install \ 127 --enable-cross-compile \ 128 --with-protoc=$HOME/armnn-devenv/google/x86_pb_install/bin/protoc 129 make install -j16 130 cd .. 131 ``` 132 133#### <a name="downloadTF">Download TensorFlow</a> 134* Clone TensorFlow source code: 135 136 ```bash 137 cd ~/armnn-devenv/google/ 138 git clone https://github.com/tensorflow/tensorflow.git 139 cd tensorflow/ 140 git checkout fcc4b966f1265f466e82617020af93670141b009 141 ``` 142 143#### <a name="buildArmNN">Build ArmNN</a> 144 145* Clone ArmNN source code: 146 147 ```bash 148 cd ~/armnn-devenv/ 149 git clone https://github.com/ARM-software/armnn.git 150 ``` 151 152* Checkout ArmNN branch: 153 154 ```bash 155 cd armnn 156 git checkout <branch_name> 157 git pull 158 ``` 159 160 For example, if you want to checkout release branch of 20.02: 161 ```bash 162 git checkout branches/armnn_20_02 163 git pull 164 ``` 165 166* Generate TensorFlow protobuf definitions: 167 168 ```bash 169 cd ~/armnn-devenv/google/tensorflow 170 ~/armnn-devenv/armnn/scripts/generate_tensorflow_protobuf.sh \ 171 $HOME/armnn-devenv/google/tf_pb $HOME/armnn-devenv/google/x86_pb_install 172 ``` 173 174 * Build ArmNN: 175 176 (Requires CMake if not previously installed: `sudo apt install cmake`) 177 178 ```bash 179 mkdir ~/armnn-devenv/armnn/build 180 cd ~/armnn-devenv/armnn/build 181 CXX=aarch64-linux-android<Android_API>-clang++ \ 182 CC=aarch64-linux-android<Android_API>-clang \ 183 CXX_FLAGS="-fPIE -fPIC" \ 184 cmake .. \ 185 -DCMAKE_ANDROID_NDK=$NDK \ 186 -DCMAKE_SYSTEM_NAME=Android \ 187 -DCMAKE_SYSTEM_VERSION=<Android_API> \ 188 -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a \ 189 -DCMAKE_EXE_LINKER_FLAGS="-pie -llog -lz" \ 190 -DARMCOMPUTE_ROOT=$HOME/armnn-devenv/ComputeLibrary/ \ 191 -DARMCOMPUTE_BUILD_DIR=$HOME/armnn-devenv/ComputeLibrary/build \ 192 -DBOOST_ROOT=$HOME/armnn-devenv/boost/install/ \ 193 -DARMCOMPUTENEON=1 -DARMCOMPUTECL=1 -DARMNNREF=1 \ 194 -DTF_GENERATED_SOURCES=$HOME/armnn-devenv/google/tf_pb/ -DBUILD_TF_PARSER=1 \ 195 -DPROTOBUF_ROOT=$HOME/armnn-devenv/google/arm64_pb_install/ 196 ``` 197 198 To include standalone sample dynamic backend tests, add the argument to enable the tests and the dynamic backend path to the CMake command: 199 200 ```bash 201 -DSAMPLE_DYNAMIC_BACKEND=1 \ 202 -DDYNAMIC_BACKEND_PATHS=$SAMPLE_DYNAMIC_BACKEND_PATH 203 ``` 204 Where $SAMPLE_DYNAMIC_BACKEND_PATH is the path where libArm_SampleDynamic_backend.so library file is pushed 205 206 * Run the build 207 ```bash 208 make -j16 209 ``` 210 211#### <a name="buildStandaloneBackend">Build Standalone Sample Dynamic Backend</a> 212* The sample dynamic backend is located in armnn/src/dynamic/sample 213 ```bash 214 mkdir build 215 cd build 216 ``` 217 218* 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 armNN build: 219 ```bash 220 #!/bin/bash 221 CXX=aarch64-linux-android<Android_API>-clang++ \ 222 CC=aarch64-linux-android<Android_API>-clang \ 223 CXX_FLAGS="-fPIE -fPIC" \ 224 cmake \ 225 -DCMAKE_SYSTEM_NAME=Android \ 226 -DCMAKE_CXX_FLAGS=--std=c++14 \ 227 -DCMAKE_EXE_LINKER_FLAGS="-pie -llog" \ 228 -DCMAKE_MODULE_LINKER_FLAGS="-llog" \ 229 -DBOOST_ROOT=$HOME/armnn-devenv/boost/install \ 230 -DBoost_SYSTEM_LIBRARY=$HOME/armnn-devenv/boost/install/lib/libboost_system.a \ 231 -DARMNN_PATH=$HOME/armnn-devenv/armnn/build/libarmnn.so .. 232 ``` 233 234* Run the build 235 ```bash 236 make 237 ``` 238 239#### <a name="runArmNNUnitTests">Run the ArmNN unit tests on an Android device</a> 240 241 242* Push the build results to an Android device and make symbolic links for shared libraries: 243 Currently adb version we have used for testing is 1.0.41. 244 245 ```bash 246 adb push libarmnnTfParser.so /data/local/tmp/ 247 adb push libarmnn.so /data/local/tmp/ 248 adb push libtimelineDecoder.so /data/local/tmp/ 249 adb push UnitTests /data/local/tmp/ 250 adb push $NDK/sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libc++_shared.so /data/local/tmp/ 251 adb push $HOME/armnn-devenv/google/arm64_pb_install/lib/libprotobuf.so /data/local/tmp/libprotobuf.so.23.0.0 252 adb shell 'ln -s libprotobuf.so.23.0.0 /data/local/tmp/libprotobuf.so.23' 253 adb shell 'ln -s libprotobuf.so.23.0.0 /data/local/tmp/libprotobuf.so' 254 ``` 255 256* Push the files needed for the unit tests (they are a mix of files, directories and symbolic links): 257 258 ```bash 259 adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/testSharedObject 260 adb push -p ~/armnn-devenv/armnn/build/src/backends/backendsCommon/test/testSharedObject/* /data/local/tmp/src/backends/backendsCommon/test/testSharedObject/ 261 262 adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/testDynamicBackend 263 adb push -p ~/armnn-devenv/armnn/build/src/backends/backendsCommon/test/testDynamicBackend/* /data/local/tmp/src/backends/backendsCommon/test/testDynamicBackend/ 264 265 adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath1 266 adb push -p ~/armnn-devenv/armnn/build/src/backends/backendsCommon/test/backendsTestPath1/* /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath1/ 267 268 adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath2 269 adb push -p ~/armnn-devenv/armnn/build/src/backends/backendsCommon/test/backendsTestPath2/Arm_CpuAcc_backend.so /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath2/ 270 adb shell ln -s Arm_CpuAcc_backend.so /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath2/Arm_CpuAcc_backend.so.1 271 adb shell ln -s Arm_CpuAcc_backend.so.1 /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath2/Arm_CpuAcc_backend.so.1.2 272 adb shell ln -s Arm_CpuAcc_backend.so.1.2 /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath2/Arm_CpuAcc_backend.so.1.2.3 273 adb push -p ~/armnn-devenv/armnn/build/src/backends/backendsCommon/test/backendsTestPath2/Arm_GpuAcc_backend.so /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath2/ 274 adb shell ln -s nothing /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath2/Arm_no_backend.so 275 276 adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath3 277 278 adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath5 279 adb push -p ~/armnn-devenv/armnn/build/src/backends/backendsCommon/test/backendsTestPath5/* /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath5/ 280 281 adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath6 282 adb push -p ~/armnn-devenv/armnn/build/src/backends/backendsCommon/test/backendsTestPath6/* /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath6/ 283 284 adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath7 285 286 adb shell mkdir -p /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath9 287 adb push -p ~/armnn-devenv/armnn/build/src/backends/backendsCommon/test/backendsTestPath9/* /data/local/tmp/src/backends/backendsCommon/test/backendsTestPath9/ 288 289 adb shell mkdir -p /data/local/tmp/src/backends/dynamic/reference 290 adb push -p ~/armnn-devenv/armnn/build/src/backends/dynamic/reference/Arm_CpuRef_backend.so /data/local/tmp/src/backends/dynamic/reference/ 291 ``` 292 293 If 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 ArmNN is built. 294 This is the example when $SAMPLE_DYNAMIC_BACKEND_PATH is specified as /data/local/tmp/dynamic/sample/: 295 296 ```bash 297 adb shell mkdir -p /data/local/tmp/dynamic/sample/ 298 adb push -p ${WORKING_DIR}/armnn/src/dynamic/sample/build/libArm_SampleDynamic_backend.so /data/local/tmp/dynamic/sample/ 299 ``` 300 301* Run ArmNN unit tests: 302 303 ```bash 304 adb shell 'LD_LIBRARY_PATH=/data/local/tmp:/vendor/lib64:/vendor/lib64/egl /data/local/tmp/UnitTests' 305 ``` 306 307 If libarmnnUtils.a is present in `~/armnn-devenv/armnn/build/` and the unit tests run without failure then the build was successful. 308