microsoft / microsoft/onnxruntime

[Build] C++ API cannot be reliably linked with an program using CMake

Open
#21,351 1 comment 0 reactions 0 assignees View on GitHub
api build contributions welcome
Dominant language
C++
Stars
21.9k
Forks
4.2k
Avg merge
4d 11h
Merged PRs (30d)
184

Description

### Describe the issue

Hello,
Thank you for maintaining this project.
I am having issues trying to link my target C++ program with ONNX Runtime using CMake.

**Context**: I want to statically link ONNX Runtime with a C++ inference example (for instance: https://github.com/microsoft/onnxruntime-inference-examples/blob/main/c_cxx/MNIST/MNIST.cpp). I compiled ONNX runtime and its dependencies as static libraries (using CMake), and I am also building the target program using CMake. Below I added the CMake command I used to compile ONNX Runtime as well as my own building script.

**Issue**: It seems that ONNX Runtime does not generate any `FindOnnxruntime.cmake` file, nor a `onnxruntimeConfig.cmake` file. Because there are a tons of dependencies compiled and generated, this makes it very difficult for me to then compile my own program and statically link all required libraries. I was able to make one example work by manually adding a `onnxruntimeConfig.cmake` file, then manually add a lot of `.a` filepaths to my `CMakeLists.txt`, but this is very unpractical (for instance, whenever I change the model, new dependencies must be added.

Am I missing something? The ideal scenario would be that, given a `model.onnx` file, I compile just the required features (using the `--include_ops_by_config` flag), then simply use something like `find_package(onnxruntime CONFIG REQUIRED)` in my CMake file to automatically add and compile needed libraries. There is a probably a "simple" way to do that, but I spent quite some time on it and could not find it (probably due to my lack of experience with CMake projects).

Related issues:
- #3124 , in particular [this message](https://github.com/microsoft/onnxruntime/issues/3124#issuecomment-676239644), however I did not succeed to make it work
- #10818

Any help appreciated! Thanks

### Urgency

_No response_

### Target platform

Linux x86_64

### Build script

To compile ONNX Runtime I just run `./build.sh --config=Release --minimal_build --skip_tests --parallel 12` in the general case.

To compile my sample app, the only way I found was to:
- add a custom `onnxruntimeConfig.cmake` (see below) in ./build/Linux/Release (I forgot where I found this sorry)
- use the following `CMakeLists.txt` file (the amount/names of libraries to be imported, in particular from the absl lib, largely depends on the model compiled. I have attached the sample code corresponding to this particular `CMakeLists.txt`)
- simply run `cmake .` then `make`. If there are errors "undefined reference to ...", I jut have to manually find which library it comes from, and add it in the file.

**onnxruntimeConfig.cmake**
```
get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE)

get_filename_component(CMAKE_CURRENT_LIST_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
get_filename_component(onnxruntime_INSTALL_PREFIX "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE)

set(onnxruntime_INCLUDE_DIRS ${onnxruntime_INSTALL_PREFIX}/include)
set(onnxruntime_LIBRARIES onnxruntime)
set(onnxruntime_CXX_FLAGS "") # no flags needed

macro(set_and_check _var _file)
set(${_var} "${_file}")
if(NOT EXISTS "${_file}")
message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !")
endif()
endmacro()

macro(check_required_components _NAME)
foreach(comp ${${_NAME}_FIND_COMPONENTS})
if(NOT ${_NAME}_${comp}_FOUND)
if(${_NAME}_FIND_REQUIRED_${comp})
set(${_NAME}_FOUND FALSE)
endif()
endif()
endforeach()
endmacro()

####################################################################################
#include("${CMAKE_CURRENT_LIST_DIR}/onnxruntimeTargets.cmake")
```

**CMakeLists.txt**
```
cmake_minimum_required(VERSION 3.5)
project(model)
set(CMAKE_CXX_STANDARD 17 )

set(ONNXRUNTIME "/home/vagrant/onnxruntime/build/Linux/Release")
list(APPEND CMAKE_PREFIX_PATH ${ONNXRUNTIME})
find_package(onnxruntime REQUIRED)

set(PROTOBUF "/home/vagrant/onnxruntime/build/Linux/Release/_deps/protobuf-build")
list(APPEND CMAKE_PREFIX_PATH ${PROTOBUF})
find_package(Protobuf REQUIRED)

include_directories(${onnxruntime_INCLUDE_DIRS})
include_directories(${onnxruntime_INCLUDE_DIRS}/onnxruntime/core/session)

set(onnxruntime_LIBRARIES
-Wl,--no-whole-archive
-Wl,--start-group
${ONNXRUNTIME}/libonnxruntime_common.a
${ONNXRUNTIME}/libonnxruntime_flatbuffers.a
${ONNXRUNTIME}/libonnxruntime_framework.a
${ONNXRUNTIME}/libonnxruntime_graph.a
${ONNXRUNTIME}/libonnxruntime_mlas.a
${ONNXRUNTIME}/libonnxruntime_optimizer.a
${ONNXRUNTIME}/libonnxruntime_providers.a
${ONNXRUNTIME}/libonnxruntime_session.a
${ONNXRUNTIME}/libonnxruntime_util.a
${ONNXRUNTIME}/libonnx_proto.a
${ONNXRUNTIME}/libonnx.a
${ONNXRUNTIME}/_deps/google_nsync-build/libnsync_cpp.a
-Wl,--end-group
)

set(absl_LIBRARIES
-Wl,--no-whole-archive
-Wl,--start-group
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/base/libabsl_base.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/base/libabsl_raw_logging_internal.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/base/libabsl_spinlock_wait.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/base/libabsl_malloc_internal.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/base/libabsl_throw_delegate.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/container/libabsl_raw_hash_set.a
# ${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/container/libabsl_hashtablez_sampler.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/hash/libabsl_low_level_hash.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/hash/libabsl_hash.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/hash/libabsl_city.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/strings/libabsl_str_format_internal.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/strings/libabsl_strings.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/strings/libabsl_strings_internal.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/synchronization/libabsl_synchronization.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/synchronization/libabsl_kernel_timeout_internal.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/time/libabsl_time.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/time/libabsl_civil_time.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/time/libabsl_time_zone.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/numeric/libabsl_int128.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/debugging/libabsl_stacktrace.a
${ONNXRUNTIME}/_deps/abseil_cpp-build/absl/debugging/libabsl_debugging_internal.a
-Wl,--end-group
)

set(other_LIBRARIES
-Wl,--no-whole-archive
-Wl,--start-group
${ONNXRUNTIME}/_deps/pytorch_cpuinfo-build/libcpuinfo.a
${ONNXRUNTIME}/_deps/re2-build/libre2.a
-Wl,--end-group
)

message(STATUS "onnxruntime_INCLUDE_DIRS ${onnxruntime_INCLUDE_DIRS}")
message(STATUS "onnxruntime_LIBRARIES ${onnxruntime_LIBRARIES}")
message(STATUS "PROTOBUF_LIBRARIES ${PROTOBUF_LIBRARIES}")

add_executable(model model.cpp)

target_link_libraries(model
${onnxruntime_LIBRARIES}
${PROTOBUF_LIBRARIES}
${other_LIBRARIES}
${absl_LIBRARIES}
)
```

**The code I use to generate model.onnx (this varies depending on the model I want, and so do the dependencies later required in CMakeLists.txt**
```
import torch

class SimpleMLP(torch.nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super(SimpleMLP, self).__init__()
self.fc1 = torch.nn.Linear(input_size, hidden_size)
self.relu = torch.nn.ReLU()
self.fc2 = torch.nn.Linear(hidden_size, output_size)

def forward(self, x):
out = self.fc1(x)
out = self.relu(out)
out = self.fc2(out)
return out

# Instantiate and train your model
model = SimpleMLP(10,20,50)

# Train your model here

# Dummy input for the export
dummy_input = torch.randn(1, 10)

# Export the model
torch.onnx.export(model, dummy_input, "model.onnx",
input_names=['input'], output_names=['output'],
dynamic_axes={'input': {0: 'batch_size'}, 'output': {0: 'batch_size'}})
```

### Error / output

If I don't manually add a `onnxruntimeConfig.cmake` file, I get an error:

```
CMake Error at CMakeLists.txt:8 (find_package): By not providing "Findonnxruntime.cmake" in CMAKE_MODULE_PATH this project
has asked CMake to find a package configuration file provided by
"onnxruntime", but CMake did not find one.
onnxruntimeConfig.cmake
onnxruntime-config.cmake
```

Once I add it, I usually get a lot of `undefined reference to ()` errors, until I add it manually in my CMakeLists.txt. Once I added all required dependencies, it works well, until I want to use a different model.

### Visual Studio Version

n.a.

### GCC / Compiler Version

g++ 11.4 / cmake 3.30.0

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the static-linking setup from build.sh and the sample CMakeLists.txt, including the generated onnxruntimeConfig.cmake and model.cpp. Trace how the build currently exposes ONNX Runtime and its dependencies, then verify that a clean CMake configuration can link the sample without manually listing model-specific libraries.

Written by the indexing model from the issue text.

Assessment

Tech stack
cmake, cpp, python, pytorch
Domain
api, build-system
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.