libtensorflow_inference r1.15 を Android 向けにビルドするメモ

## 背景

* C/C++ で, 既存 r1.x 系の Tensorflow(C binding) 推論アプリを Android でも動かしたい 
  * 速度はそんなに問わないし, そもそも tflite では動かないモデルがある
  * Java レイヤーは使わない

tensorflow + Android で検索するとだいたい tflite がヒットしてしまいますが, ここでは本来の(?)libtensorflow を Android 向けにビルドしてみます.
   
モバイルでは tflite 推奨で, libtensorflow のモバイル向けビルドは非推奨となっています.
とはいえ, そもそも tflite では動かないケースが多いので(特に RNN とか使っていたりする音声系), 頑張って libtensorflow を動かしてみます.

libtensorflow on Android では, CPU のみの対応になります.
現状 C++ API のみのようです. また, 使える C++ API には制約があります
(C API は根本からビルドを変えないといけないことが分かりましたので, 現状無理(or Bazel ファイルを頑張って解析&改修する必要がある))

TensorFlow は r2.x が出ていますが, ここでは r1.15 を使います.

## ビルド方法(Bazel)

TensorFlow の `./configure` で, Android WORKSPACE の作成を yes にしておきます.

今回は ndk r21, Android API level 26, Android SDK tool 29 あたりを使いました.
(2020 年 02 月 12 日での概ね最新)
 
https://github.com/tensorflow/tensorflow/blob/master/tensorflow/tools/ci_build/builds/android_full.sh

を参考にビルドします.

```
    bazel build --config=monolithic --cpu=arm64-v8a \
        --compilation_mode=opt --cxxopt=-std=c++11 \
        --crosstool_top=//external:android/crosstool \
        --host_crosstool_top=@bazel_tools//tools/cpp:toolchain \
        //tensorflow/core:android_tensorflow_lib \
        //tensorflow/tools/android/inference_interface:libtensorflow_inference.so \
        //tensorflow/examples/android:libtensorflow_demo.so \
        //tensorflow/tools/benchmark:benchmark_model
```

という感じで .so までビルドできるはずです. ビルドには, Threadripper 1950X で 10 分ほど時間かかります. libtensorflow_inference.so まででよくて, 最後のほうは動作確認用などとしてお好みで.

C++ std は, tf のバージョンによって変わります. v1.15.2 では c++11 でした. v2 では c++14 でした.

`benchmark_model` までビルドしている場合, `benchmark_model` を  `/data/local/tmp` などに adb push して走らせて動くかどう確認しましょう

https://github.com/tensorflow/tensorflow/tree/r1.15/tensorflow/tools/benchmark

## 自前アプリに組み込む

TensorFlow C++(libtensorflow_cc.so) で推論アプリを CMake でビルドする(r1.8 対象)
https://qiita.com/syoyo/items/c102611ff63a6bbadc80

を参考に, ヘッダファイルへパスを通し(eigen, protobuf, absl は bazel のビルドディレクトリにあるものへパスを通す), libtensorflow_inference.so と, `bazel-genfiles/external/com_google_protobuf` あたりにある `libprotobuf.pic.a`, `libprotobuf_lite.pic.a` をリンクすればいけるはずです!

(libprotobuf と libprotobuf_lite の関係性は不明だが, 両方をリンクしないとうまくいかない).

もし, 他にリンク時にシンボルが見つからないエラーがでるようであれば,

Linux で, 複数 .a, .so のどこに C++ シンボルが定義されているか調べる
https://qiita.com/syoyo/items/cac44f37832b5f0e2bac

や, bazel のビルドログ表示(`--subcommands` or `-s`)でどのライブラリとリンクすればいいのかチェックしてみましょう. 

NDK のバージョンは, libtensorflow_inference をビルドしたときのものと合わせておきましょう
(そうでないと, 特に protobuf 周りでエラーが出る)

### C API ?

Android プロジェクトであればあまり ABI などでのリンクエラーは出ないと思うので, TF の呼び出しを C++ で書いてもいいと思いますが, Windows での TF アプリとコードを一緒にしたい(Windows では C API でないといろいろリンクエラーなど出てめんどいため)などで, C API を使いたいときもあります.

Android の `libtensorflow_inference.so` 用 Bazel BUILD ファイルでは, C API のライブラリは含まれていませんでした.

自前で `tensorflow/c` にある c_api.cc あたりのファイルをアプリに取り込んでビルドする必要がありそうですが, 現状足りないシンボルエラーが出てきてうまくいきません.
 
libtensorflow_inference.so を作る時点で, 足りない(private になっている?) ソースコードを追加しないとダメそうです. いろいろ調べるのがめんどいので, 現状は C++ API で頑張りましょう.

## TODO

* [ ] C API interface の .so を作る.
* [ ] Makefile ベースのビルドを試す(`tensorflow/contrib/makefile`)
* [ ] TensorFlow r2.x で試す
* [ ] java バインディング含んだ prebuilt .so が C++ アプリにそのままつかえるか試す(ヘッダファイルを .so の tf version と合わせればいけそう)
* [ ] 相変わらず bazel + protobuf 周りでエラー出たりで tensorflow 辛すぎるので, はやく **libtorch mobile に移行して幸せになりたい**