The Coding Notebook
Memorable coding moments of a software engineer
Building TensorFlow Lite for Android on WSL
In this post I am going to document the process of building the TensorFlow Lite android library and c++ library (libtensorflowlite) on Ubuntu using WSL (Windows Subsystem for Linux). In the process we will also install the Android SDK and NDK



This guide is based on 2 TF guides:

[Build TensorFlow Lite for ARM64 boards](https://www.tensorflow.org/lite/guide/build_arm64)

[Android quickstart](https://www.tensorflow.org/lite/guide/android)

The tensorflow project is progressing quite rapidly, and any guide written today might not work the same next week, so this guide is good as of **25-JAN-2020** using tensorflow **v2.0.0**.

# Environment Setup
Start by cloning tensorflow and switching to tag v2.0.0, this I will run on **Windows** side (not wsl), so the files will be accessible from Windows.
```sh
git clone git@github.com:tensorflow/tensorflow.git
cd tensorflow
git checkout v2.0.0
```
Next, open wsl shell, and install required prereq:
```sh
sudo apt-get update
sudo apt-get install crossbuild-essential-arm64
sudo apt-get install unzip
```
Now cd into the tensorflow folder, on wsl your windows files are accessible on `/mnt`, so for me:
`cd /mnt/c/MyProjects/tensorflow`

Next download dependencies:
`./tensorflow/lite/tools/make/download_dependencies.sh`
If successful all deps were extracted to: `./tensorflow/lite/tools/make/downloads`

# Installing Bazel
Tensorflow is using Bazel build system, we need to install it, but we can't just install whatever version we want, need to check what is supported by TF, this is defined in `configure.py`:
`cat configure.py | grep BAZEL_VERSION`
and look for `_TF_MIN_BAZEL_VERSION` and `_TF_MAX_BAZEL_VERSION` - I'll pick the max version, which is currently 0.26.1

Visit Bazel docs for [installing on Ubuntu](https://docs.bazel.build/versions/master/install-ubuntu.html), there are 2 methods, I'll choose the method: "Installing using binary installer".
```sh
cd ~
sudo apt-get install pkg-config zip g++ zlib1g-dev unzip python3
wget https://github.com/bazelbuild/bazel/releases/download/0.26.1/bazel-0.26.1-installer-linux-x86_64.sh
chmod +x bazel-0.26.1-installer-linux-x86_64.sh
./bazel-0.26.1-installer-linux-x86_64.sh --user
```
Update your shell startup script (e.g .bashrc) and add `$HOME/bin` to the path:
`export PATH="$PATH:$HOME/bin"`
Close the current shell and open a new one, check bazel version:
`bazel version`
It should print the version you installed...

# Installing Android SDK and NDK
In order to build TF libraries we need to install Android SDK, NDK and Build Tools, this is a bit tricky...
First decide where you'll want to install all the components, we'll call it the **android root** folder, I'll create mine under home dir:
```sh
cd ~
mkdir android
```

# Installing NDK
Next, check what are the supported NDK versions, this info is in the file `tensorflow/configure.py`, for example:
`cat /mnt/c/MyProjects/tensorflow/configure.py | grep SUPPORTED_ANDROID_NDK_VERSIONS` - as of now I will install the latest supported: v18
Go to the [revisions history page](https://developer.android.com/ndk/downloads/revision_history), and follow the link of the version you need, there will be a "terms" page you'll have to accept and finally copy the link to the "**Linux 64-bit (x86)**" file and download it, for me the file is: `android-ndk-r18b-linux-x86_64.zip`
```sh
cd ~
wget [The link you copied for the zip file]
unzip [downloaded zip file] -d [your android root dir]
```
This should create an "android-ndk-xxx" folder under your android root dir.

# Installing SDK
Go to the [download page](https://developer.android.com/studio#downloads) and scroll down to "Command line tools only" section. Click on the "Linux" file, and terms modal will pop, mark the "I agree" checkbox (if you agree), and do NOT click on the "Download" button, instead RightClick-->Copy link address.
Back on the WSL shell:
```sh
cd ~
wget [address to sdk tools zip file]
mkdir -p [your android root dir]/cmdline-tools
unzip [downloaded zip file] -d [your android root dir]/cmdline-tools
```
This should create a "tools" folder under your android root dir.

## Installing java
If java is not installed on your machine install OpenJDK8:
```sh
sudo apt install openjdk-8-jdk openjdk-8-jre
```

If java is already installed, note that as of today, the latest android sdk tools do NOT work with Java 11, so first try and see.
Assume your android root dir is "~/android", run:
```sh
cd ~
./android/cmdline-tools/tools/bin/sdkmanager --version
```
If you get an error saying "bla bla XmlSchema", most likely this is due incompatible java version...
Go ahead and install OpenJDK 8:
```sh
sudo apt install openjdk-8-jdk openjdk-8-jre
```
This will NOT overwrite your current java (if exists), so if you run `java -version` you'll still see 11 (or later), but since we just need this to work with the `sdkmanager` script, we are going to edit that script and set `JAVA_HOME` there:
```sh
vi android/cmdline-tools/tools/bin/sdkmanager
```
Search for where `DEFAULT_JVM_OPTS` is defined and just before add:
```sh
JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-amd64
```
Now running `./android/cmdline-tools/tools/bin/sdkmanager --version` should work!

## Using sdkmanager
First list all available packages:
```sh
cd ~/android
./cmdline-tools/tools/bin/sdkmanager --list
```
This will output all available packages, we need to install `build-tools` and `platforms`.
Use the latest version available, for me it is 29.0.0 (29.0.2 is also available but I picked the major)
```sh
cd ~/android
./cmdline-tools/tools/bin/sdkmanager "build-tools;29.0.0"
./cmdline-tools/tools/bin/sdkmanager "platforms;android-29"
```
Next we need to define several environment variables that will be used by the build system, I do it under my `.bashrc` file
```sh
export ANDROID_NDK_HOME="path to the installed ndk folder"
export ANDROID_NDK_API_LEVEL="installed ndk version"
export ANDROID_BUILD_TOOLS_VERSION="installed build-tools version"
export ANDROID_SDK_API_LEVEL="install platforms version"
export ANDROID_SDK_HOME="your android root dir"
```
For me it looks like this:
```sh
export ANDROID_NDK_HOME="/home/yuval/android/android-ndk-r18b"
export ANDROID_NDK_API_LEVEL="18"
export ANDROID_BUILD_TOOLS_VERSION="29.0.0"
export ANDROID_SDK_API_LEVEL="29"
export ANDROID_SDK_HOME="/home/yuval/android"
```
Close all WSL shells and open new ones (or just "source" the bashrc, but safer to re-open the shell).

# Configure Bazel build
Run the `./configure` script in the tensorflow root directory (for me: /mnt/c/MyProjects/tensorflow), and answer "Y" when the script asks to interactively configure the ./WORKSPACE for Android builds:
```sh
cd [tensorflow root dir]
./configure
```
The script will asks lots of questions, I just accepted all the default options (except of course where it asks to interactively configure the ./WORKSPACE for Android builds)

## Python vs Python3
Although the script above asked for the exact python binary to use, the build will still fail if `python` is not in the PATH (and is not the same as configured above). This is a real issue, I didn't find any good solution but to symlink `python-->python3`, yes, this can be an issue if `python` is actually python 2.7 on your machine... to do the symlink:
```sh
sudo ln -s /usr/bin/python3 /usr/bin/python
```

# Build tensorflow-lite AAR
The moment of truth... lets try to build the TF lite android lib, from the tensorflow root directory run:
```sh
bazel build --cxxopt='-std=c++11' -c opt --fat_apk_cpu=arm64-v8a //tensorflow/lite/java:tensorflow-lite
# Here I built just for the arm64 arch, but you can specify multiple options like: x86,x86_64,arm64-v8a,armeabi-v7a
```
If all was good, the output will be `bazel-bin/tensorflow/lite/java/tensorflow-lite.aar`

# Build libtensorflow C++ shared object
```sh
bazel build //tensorflow/lite:libtensorflowlite.so --config=android_arm64 --cxxopt='--std=c++11' -c opt
# Here also you can specify different arch (1 command at a time): android_arm, android_x86, android_x86_64,
```
If all was good, the output will be `bazel-bin/tensorflow/lite/libtensorflowlite.so`

# The End
I really hope this worked out well for you, tensorflow docs are getting better and better every day, so keep checking them!