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?
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
.