Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CMake - how to organize dependencies

Tags:

c++

cmake

So I'm trying to understand what goes wrong with one of my C++ projects. Essentially, project 1 works ok, everything is great. In the main header file of the project I have

#include "spdlog/spdlog.h"

And I have spdlog as a subproject in project 1. Additionally in my CMake for project 1 I have include_directories(spdlog/include). Now, I'm building project 2, which depends on project 1 and has it as subproject. However, when I try to include spdlog it does not allow me and wants me to make the full ../project1/spdlog/include/spdlog.h. What is the correct way to organize this dependency and to have the header included?

like image 513
Alex Botev Avatar asked Nov 09 '22 14:11

Alex Botev


1 Answers

You should look for modern cmake resources. In the modern cmake style you can create an Imported target for spdlog which you can then use everywhere else.

lets assume the following structure

external/spdlog
external/CMakeLists.txt
project1/CMakeLists.txt
project2/CMakeLists.txt
CMakeLists.txt

in the external/CMakeLists.txt you write

## add the imported library which is 
##   an INTERFACE (ie header only)
##   IMPORTED (ie third party - no compilation necessary)
##   GLOBAL (you can reference it from everywhere 
##           even if the target is not in the same scope
##           which is the case for project1 and project2 because 
##           the spdlog  target is not in their parent folder)
add_library(spdlog INTERFACE IMPORTED GLOBAL)
## set the include directory which is to be associated with this target
## and which will be inherited by all targets which use 
## target_link_libraries(<target> spdlog)
target_include_directories(spdlog INTERFACE spdlog/include)

in the root CMakeLists.txt you write your solution configuration (example)

project(mysolution)
add_subdirectory(external)
add_subdirectory(project1)
add_subdirectory(project2)

in your project CMakeLists.txts you can now use the target that you created in external

project1/CMakeLists.txt

add_library(project1 ${Sources})
## this will set the include directories configured in the spdlog target (library) 
## and also ensure that targets depending on target1 will have access to spdlog  
## (if PUBLIC were changed to PRIVATE then you could only use spdlog in 
## project1 and dependeing targets like project2 would not 
## inherit the include directories from spdlog it wouldn't 
target_link_libraries(project1 PUBLIC spdlog)

project2/CMakeLists.txt

add_library(project2 ${Sources})
## this will link project1 which in return also links spdlog
## so that now project2 also has the include directories (because it was inherited)
## also targets depending on project2 will inherit everything from project1 
## and spdlog because of the PUBLIC option
target_link_libraries(project2 PUBLIC project1)

sources:

[0] https://cmake.org/cmake/help/v3.9/command/add_library.html

[1] https://cmake.org/cmake/help/v3.9/command/target_link_libraries.html?highlight=target_link_libraries

[2] http://thetoeb.de/2016/08/30/modern-cmake-presentation/ (see presentation starting at slide 20)

like image 52
toeb Avatar answered Nov 14 '22 23:11

toeb