Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to build TensorFlow Lite as a static library and link to it from a separate (CMake) project?

I've successfully built a simple C++ app running TF Lite model by adding my sources to tensorflow/lite/examples, similarly to what the official C++ TF guide suggests for full TF. Now I want to build it as a separate project (shared library) linking to TF Lite statically and using CMake as a build system.

I tried to add a custom target to my CMakeLists.txt, which would build TF Lite with Bazel:

set(TENSORFLOW_DIR ${CMAKE_SOURCE_DIR}/thirdparty/tensorflow)
add_custom_target(TFLite
    COMMAND bazel build //tensorflow/lite:framework
    COMMAND bazel build //tensorflow/lite/kernels:builtin_ops
    WORKING_DIRECTORY ${TENSORFLOW_DIR})

I chose those Bazel targets because the BUILD file from tensorflow/lite/examples/minimal has them as dependencies, and they work for me when I build my code with Bazel within TF repo. Not sure if that's enough.

Then I manually collect include dirs (with that ugly temporarily hardcoded path) and libs:

set(TFLite_INCLUDES
    ${TENSORFLOW_DIR}
    ~/.cache/bazel/_bazel_azymohliad/ec8567b83922796adb8477fcbb00a36a/external/flatbuffers/include)

set(TFLite_LIBS
    ${TENSORFLOW_DIR}/bazel-bin/tensorflow/lite/libframework.pic.a)
    
target_include_directories(MyLib ... PRIVATE ... ${TFLite_INCLUDES})
target_link_libraries(MyLib ... ${TFLite_LIBS})

And with this configuration, I get many undefined references to TFLite stuff during linkage. I checked with nm and those symbols are indeed missing in libframework.pic.a, I found some of them in various .o files in Bazel output. Manually picking all those .o files seems wrong.

So, is it possible to link nicely to TF Lite from CMake like I'm trying to? Maybe is there some magical bazel query include_dirs(//tensorflow/lite:framework) command which would give me paths to all the necessary include dirs, and a similar command for libraries to link against so that I could pass this info to CMake?

like image 910
Andrii Zymohliad Avatar asked Mar 12 '19 16:03

Andrii Zymohliad


1 Answers

I ended up listing all necessary TFLite object files manually for CMake's target_link_libraries (in the TFLite_LIBS) and it works.

I used a simple shell script to get the list of necessary object files. First I collected all undefined references from build log into a bash-array as following:

SYMBOLS=(\
    'tflite::CombineHashes('\
    'tflite::IsFlexOp('\
    'tflite::ConvertArrayToTfLiteIntArray('\
    'tflite::EqualArrayAndTfLiteIntArray('\
    ...
    'tflite::ConvertVectorToTfLiteIntArray(')

Then for every symbol in that array, I went through every *.o file in bazel build output:

for SYMBOL in $SYMBOLS[@]; do
    for OBJ in $(find -L /path/to/tensorflow/bazel-bin/ -name '*.o'); do
        nm -C $OBJ | grep "T $SYMBOL" > /dev/null && echo $OBJ
    done
done | sort | uniq

and added the output to TFLite_LIBS in CMake (with correct path prefix, of course). After that, I got a new portion of undefined references, but after a few iterations, it resolved everything.

Probably I could also obtain the full list of dependencies from *-params file from my initial in-tree build, but a quick check showed that it had some redundant items, and the script collected only the necessary ones.

For include locations, I replaced that hardcoded path to flatbuffers in bazel cache with ${TENSORFLOW_DIR}/bazel-tensorflow/external/flatbuffers/include/. Thanks jdehesa for the hint.

UPDATE:
The native build of all-inclusive TF Lite static library can be done very similar to official build instructions for RPi, iOS or ARM64 using plain old make:
1. ./tensorflow/lite/tools/make/download_dependencies.sh
2. make -f tensorflow/lite/tools/make/Makefile

The output library will be stored as <tensorflow-root>/tensorflow/lite/tools/make/gen/<platform>/lib/libtensorflow-lite.a. And the external dependencies with their headers would go into <tensorflow-root>/tensorflow/tensorflow/lite/tools/make/downloads (for example flatbuffers headers are in <tensorflow-root>/tensorflow/tensorflow/lite/tools/make/downloads/flatbuffers/include).

Guide doesn't mention that make could be called directly. There are wrapper-scripts for different cross-compilation targets, which just set appropriate variables and run make. But by default make would just do native build. This make invokation can be added as a custom command in CMakeLists.txt.

like image 96
Andrii Zymohliad Avatar answered Sep 17 '22 05:09

Andrii Zymohliad