Problem description
Hi everyone! I’m starting the new project and I would like to make it using CMake in a good way from the beginning.
I’ve found the proposition of project structure at the modern CMake book and It look’s that it has sense but unfortunately I can’t found a nice example of how to make it alive.
If there is a more common or modern structure that should be used please point it out.
Based on instructions from the mentioned book I’ve created a similar but simplified (for now) directories structure.
Proposed directories structure
I want to logically separate external libraries from the application, use separate CMakeLists.txt
to build the external modules and I won’t use git submodules because as I’ve read somewhere the FetchContent_Declare
should be used instead.
I want to create the following directories structure:
.
├── CMakeLists.txt
├── external
│ └── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.cpp
-
external
directory will contain external libraries (like cli11, spdlog, etc.) which should be available in thesrc
directory. -
src
directory will contain the main application.
And this is how the CMakeLists
looks like:
-
Root
CMakeLists.txt
:
cmake_minimum_required(VERSION 3.16)
project(
KillingRobot
VERSION 0.1
DESCRIPTION "A self-aware robot who kills people who don't know CMake"
)
add_subdirectory(external)
add_subdirectory(src)
-
external
CMakeLists.txt
:
(I’m not sure if the separate requirements should be in the one CMakeLists.txt file or rather split into com.in
files included inCMakeLists.txt
.)
cmake_minimum_required(VERSION 3.16)
# Download libraries used in the project.
include(FetchContent)
# ----------------------------------------------------------
# A fast and simple to use logging library.
FetchContent_Declare(
spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.8.2)
FetchContent_GetProperties(spdlog)
if(NOT spdlog_POPULATED)
message("Cloning spdlod")
FetchContent_Populate(spdlog)
add_subdirectory(
${spdlog_SOURCE_DIR}
${spdlog_BINARY_DIR})
endif()
# ----------------------------------------------------------
# A command line argument parser.
FetchContent_Declare(
cli11
GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git
GIT_TAG v1.9.1)
FetchContent_GetProperties(cli11)
if(NOT cli11_POPULATED)
message("Cloning CLI11")
FetchContent_Populate(cli11)
add_subdirectory(
${cli11_SOURCE_DIR}
${cli11_BINARY_DIR})
endif()
-
src
CMakeLists.txt
:
cmake_minimum_required(VERSION 3.16)
add_executable(${CMAKE_PROJECT_NAME} main.cpp)
And below how the main.cpp
file looks like:
#include "spdlog/spdlog.h"
#include "CLI/App.hpp"
#include <string>
int main(int argc, char *argv[]) {
CLI::App app{"App description"};
std::string filename = "default";
app.add_option("-f,--file", filename, "A help string");
CLI11_PARSE(app, argc, argv);
spdlog::info("Filename: {0}", filename);
}
Choosen libraries
In the described example I’m using two libraries intentionally because the spdlog
is a library where including a header file is enough for building but in the case of cli11, the built library should be linked by a linker.
Downloaded libraries
After cmake
the repositories are downloaded and stored in build/_deps
directory as:
.
├── build
│ ├── _deps
│ │ ├── cli11-build
│ │ ├── cli11-src
│ │ ├── cli11-subbuild
│ │ ├── spdlog-build
│ │ ├── spdlog-src
│ │ └── spdlog-subbuild
(...)
Of course, it doesn’t build because the compiler can’t find the headers and library - it isn’t defined anywhere.
What is needed?
I’m pretty sure that I should use:
-
target_link_libraries
for linking libraries -
target_include_directories
for including the headers
Unfortunately, I can’t find a similar example (which may indicate that I’m doing something wrong). And I’m not sure if should I use them in: src/CMakeLists.txt
or rather in the root CMakeLists.txt
and the propagate it down to src/CMakeLists.txt
.
Questions
At the end I have some questions:
- How to manage includes in this case?
- How to manage libraries in this case?
- Should I use
FetchContent_Declare
or maybegit submodules
as many other projects on github? -
FetchContent_Declare
is ok for downloading libraries used in includes or rather should be used to download standalone modules likegoogletest
or other things independent from the application?
Best regards,
Maciej