J.Paravicini
J.Paravicini

Reputation: 892

CMake nested libraries

I have a Project in c++ where my executable depends on my core library and that library depends on another external library, libtorrent if it is of any relevance.

The problem is I can not figure out how to setup my CMakeLists.txt, I have been searching for days now, without avail.

The main idea is that the core library and the executable are on seperate git repositories, so the library is not a sub git module of the executable.

Preferably I would be able to use ExternalProject_Add to add the core library to the executable but when I do this, the executable complains that it does not know anything about libtorrent which again the core library uses. Adding the headers of libtorrent to the executable wasn't enough, I would also need to link the executable to libtorrent. But why would I compile the core library then anyway, since I just have to add all dependencies of the core library to the executable again.

If someone can point me in the right direction of how to setup a project that uses a core library that has other dependencies and an executable that uses the core library.

Upvotes: 2

Views: 2424

Answers (1)

Guillaume Racicot
Guillaume Racicot

Reputation: 41750

Usually, this is represented with targets and links between them.

Your setup could look like this:

#  core cmake file
find_package(LibtorrentRasterbar REQUIRED)

add_library(core_lib file1.cpp file2.cpp file3.cpp ...)
target_link_libraries(core_lib PUBLIC LibtorrentRasterbar::torrent-rasterbar) # see below
# app cmake file

find_package(core_lib) # find it elsewhere

add_executable(app file1.cpp file2.cpp file3.cpp ...)
target_link_libraries(exec PRIVATE core_lib::core_lib)

If core needs new libraries in it's headers, then you should add them to the dependencies of core_lib. Any public requirement is transitively propagated to users, like the app target.

The depedencies with external libraries or build tree is expressed using find_library. It can be a library installed in your root, installed in your user directory installed in a subdirectory of your project or simply the build tree of the library. In your case, finding the build tree is probably what you want.

Then since your core_lib library sits in another project, I suggest to go looking at how to export targets from a build tree or an installation, so find_package(core_lib) would work.


Unfortunately Libtorrent does not appear to support CMake properly, so the package Libtorrent won't be found and target Libtorrent::torrent-rasterbar won't be defined.

There are ways to work around by trying their FindLibtorrentRasterbar.cmake.

Looking at their find module, it's clear it's not made with modern cmake in mind. You'll have to add these lines at the end if the file to support linking to their targets:

if(LibtorrentRasterbar_FOUND)
    set(LibtorrentRasterbar_LIBRARY_DEPS "${LibtorrentRasterbar_LIBRARIES}")
    list(REMOVE_ITEM LibtorrentRasterbar_LIBRARY_DEPS ${LibtorrentRasterbar_LIBRARY})

    if(LibtorrentRasterbar_USE_STATIC_LIBS)
        add_library(LibtorrentRasterbar::torrent-rasterbar STATIC IMPORTED GLOBAL)
    else()
        add_library(LibtorrentRasterbar::torrent-rasterbar SHARED IMPORTED GLOBAL)
    endif()

    set_target_properties(LibtorrentRasterbar::torrent-rasterbar PROPERTIES
        IMPORTED_LOCATION ${LibtorrentRasterbar_LIBRARY}
        INTERFACE_LINK_LIBRARIES ${LibtorrentRasterbar_LIBRARY_DEPS}
        INTERFACE_COMPILE_DEFINITIONS ${LibtorrentRasterbar_DEFINITIONS}
        INTERFACE_INCLUDE_DIRECTORIES ${LibtorrentRasterbar_INCLUDE_DIRS}
    )
endif()

One detail I didn't mention is that find_package won't try to find the packages of your dependency. To do that, create a custom config file that look like this:

# cmake/core_lib-config.cmake.in
include(CMakeFindDependencyMacro)

# this line is just like a find_package
# but made for transitivity
find_dependency(LibtorrentRasterbar)
include("${CMAKE_CURRENT_LIST_DIR}/core_lib-targets.cmake")

Then change your exportation to output into core_lib-targets.cmake instead of the usual config:

# this is the new config file with the add_dependency
configure_file(
    cmake/core_lib-config.cmake.in
    core_lib-config.cmake
    @ONLY
)

# the new config file will include this target file
install(EXPORT core_lib_targets
        NAMESPACE core_lib::
        FILE core_lib-targets.cmake
        DESTINATION lib/cmake/core_lib)

# export the current build tree
export(
    EXPORT core_lib_targets
    NAMESPACE core_lib::
    FILE "${CMAKE_CURRENT_BINARY_DIR}/core_lib-targets.cmake"
)

Upvotes: 5

Related Questions