2

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.

J.Paravicini
  • 882
  • 12
  • 39
  • Standard approach is to use `find_package(libtorrent REQUIRED)` to compile your `core_lib` and `find_package(core_lib REQUIRED)` in `CMakeLists.txt` of your executable. Then your `core_lib` can either be installed in the system, or you can point CMake to look for it in your projects local directory. – pptaszni Jul 24 '19 at 15:29

1 Answers1

5

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"
)
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
  • Thank you so much for your answer, I have been trying to use their FindLibtorrentRasterbar.cmake file, however, no matter how I install libtorrent LibtorrentRasterbar remains NOT_FOUND. How could I go around this problem? – J.Paravicini Jul 24 '19 at 16:29
  • Their `FindLibtorrentRasterbar.cmake` is using package config files to get the path and interface requirement of their library. Unfortunately, I'm not really familiar with package config. – Guillaume Racicot Jul 24 '19 at 17:24
  • Ok, thanks anyway, I think with the dependencies the main problem should be fixed. I will try it out as soon as I can – J.Paravicini Jul 24 '19 at 17:52
  • So I have exported my core_lib which btw correctly found libtorrent, and it creates an exports.cmake file in the cmake-build-debug folder (I am using CLion) but I do not understand how my app should find this project now? – J.Paravicini Jul 24 '19 at 20:24
  • @J.Paravicini I have found an [excellent answer about exporting libraries here](https://stackoverflow.com/a/31537603/2104697). Also, don't fear looking at how other projects has done it. For example, [nlohmann_json](https://github.com/nlohmann/json/blob/develop/CMakeLists.txt#L102) is doing a pretty good job with it's cmake support. – Guillaume Racicot Jul 24 '19 at 20:33
  • @J.Paravicini Also, for cmake to find your other project, you'll have to set the prefix path to the build directory of your library: `cmake -DCMAKE_PREFIX_PATH=/path/to/core/build ..` although this is explained in the answer I linked – Guillaume Racicot Jul 24 '19 at 20:35
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/196971/discussion-between-j-paravicini-and-guillaume-racicot). – J.Paravicini Jul 25 '19 at 09:17