Search code examples
c++cmakedynamic-linkingdynamic-libraryintel-oneapi

Use CMake to build dependency (oneTBB as git submodule) as dynamic library?


I have a project that depends on Intel's oneTBB. My project is structured as follows:

external/
    | - CMakeLists.txt
    | - oneTBB/  (this is a git submodule)
    | - ...
include/
lib/
include/
CMakeLists.txt

I currently get things to compile by manually building oneTBB and installing it inside a prefix directory located at external/oneTBB/prefix by running the following (bash) commands:

cd oneTBB
mkdir -p prefix
mkdir -p build
cd build
cmake -DCMAKE_INSTALL_PREFIX=../prefix -DTBB_TEST=OFF ..
cmake --build .
cmake --install .

I then simply include and link using this prefix. (I got this from following the oneTBB READMEs)

While this works without issue, I'm currently trying to clean up my CMake such that its easier to build on Windows as well. Ideally, I'm looking to get to a point where I can simply run:

mkdir build
cd build
cmake ..
cmake --build .

and my project will build itself and all dependencies.

I got this working with other dependencies such as glfw and eigen by simply adding (to the CMakeLists.txt in external/:

add_subdirectory(glfw)
add_subdirectory(eigen)

But adding add_subdirectory(oneTBB) throws a LOT of warnings, starting with:

CMake Warning at external/oneTBB/CMakeLists.txt:116 (message):
  You are building oneTBB as a static library.  This is highly discouraged
  and such configuration is not supported.  Consider building a dynamic
  library to avoid unforeseen issues.

-- TBBBind build targets are disabled due to unsupported environment
-- Configuring done
CMake Warning (dev) at external/oneTBB/src/tbb/CMakeLists.txt:15 (add_library):
  Policy CMP0069 is not set: INTERPROCEDURAL_OPTIMIZATION is enforced when
  enabled.  Run "cmake --help-policy CMP0069" for policy details.  Use the
  cmake_policy command to set the policy and suppress this warning.

  INTERPROCEDURAL_OPTIMIZATION property will be ignored for target 'tbb'.
This warning is for project developers.  Use -Wno-dev to suppress it.

I have no need to build oneTBB as a static library. Am I doing something wrong in my attempt? Really all I need is for oneTBB to be built as a dynamic library and placed somewhere I can link it to (without installing it on the system overall)


Similar question:

I am also including the METIS library which depends on GKlib. Currently I'm doing this in a similar way to what I did for oneTBB where I manually build each using the following script:

# Setup the GKlib library:
cd GKlib
mkdir -p prefix
mkdir -p build
cd build
cmake -DCMAKE_INSTALL_PREFIX=../prefix ..
cmake --build . -j
cmake --install .
cd ../../

# Setup the METIS library:
cd METIS
mkdir -p prefix
make config prefix=../prefix gklib_path=../GKlib/prefix #(GKLib path is done from root, install path done relative to build)
make install -j
cd ../

When I try to add them using:

add_subdirectory(GKlib)
add_subdirectory(METIS)

it throws errors that METIS cannot find GKlib:

CMake Error at external/METIS/CMakeLists.txt:50 (add_subdirectory):
  add_subdirectory given source "build/xinclude" which is not an existing
  directory.

While I recognize this is a separate issue, I figured to include it here as it is related to my issues with add_subdirectory()


Solution

  • Many projects expect that you invoke CMake on them separately instead of adding them into an existing project with add_subdirectory. While there might be a way to make add_subdirectory work with oneTBB, there is an easier way.

    CMake has a function that allows you to download, build, and install external projects at build time: ExternalProject_Add.

    Here's an example for spdlog, taken straight from one of my own projects:

    # project_root/thirdparty/spdlog/CMakeLists.txt
    
    string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UPPER)
    
    ExternalProject_Add(spdlog-project
            GIT_REPOSITORY https://github.com/gabime/spdlog
            GIT_TAG edc51df1bdad8667b628999394a1e7c4dc6f3658
            GIT_SUBMODULES_RECURSE ON
            GIT_REMOTE_UPDATE_STRATEGY CHECKOUT
            INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/install"
            LIST_SEPARATOR |
            CMAKE_CACHE_ARGS
            "-DCMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}:STRING=${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}"
            "-DCMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER}:STRING=${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}"
            "-DCMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER}:STRING=${CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}"
            "-DCMAKE_SHARED_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER}:STRING=${CMAKE_SHARED_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}"
            "-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}"
            "-DCMAKE_INSTALL_PREFIX:STRING=<INSTALL_DIR>"
            "-DSPDLOG_BUILD_EXAMPLE:BOOL=OFF"
            )
    
    add_library(ext-spdlog INTERFACE)
    add_dependencies(ext-spdlog spdlog-project)
    ExternalProject_Get_property(spdlog-project INSTALL_DIR)
    target_include_directories(ext-spdlog SYSTEM INTERFACE "${INSTALL_DIR}/include")
    target_link_directories(ext-spdlog INTERFACE "${INSTALL_DIR}/lib")
    target_link_libraries(ext-spdlog INTERFACE spdlog$<$<CONFIG:Debug>:d>)
    

    After that, my project simply links against the created library target:

    target_link_libraries(my_project ext-spdlog)
    

    To adapt this for oneTBB, you have to switch out the repository URL and commit hash, and add your own CMake definitions (i.e. "-DTBB_TEST=OFF"). Depending on how oneTBB has its include and library directories set up, you may also have to change the target_include_directories and/or target_link_directories lines. I haven't looked this up yet, but I'm sure you can figure it out.

    This works regardless of whether the external project is built as a static or shared library. You shouldn't use git submodules, though - instead, let CMake do the downloading. (It'll only download and build once; subsequent builds will not re-build the external project if it's already built and up-to-date.)