In this segment, I shared what I’ve learned using a specific technology. It’s kind of a techno diary.

I started using CMake recently because I was looking for a solution to compile my codes without having to write long build commands or depending on an IDE to do it for me. I was afraid when I started due to the syntax, which seems complex. It wasn’t helping that there are multiple ways to get the job done. Finally, I found a method that I understand and can be used as a template for multiple projects. I was inspired by the book of John Lamp CMake tutorial [1] and of course, the CMake documentation [2].

Cmake template

Below is the folder structure. The bar library uses the foo library. With this implementation, we don’t need to specify the path of the libraries when included.

main.cpp

CMakeLists.txt

Lib Foo foo.h Foo.cpp CMakeLists.txt Bar bar.cpp bar.h CMakeLists.txt CMakeLists.txt

Build

make.sh

The CMakeLists.txt files are the configuration files. As you can see, every directory needs a CMakeList.txt file, even those who don’t have implementations files. Let’s examine all the CMakeList.txt files.



from the ./CMakeList.txt file

cmake_minimum_required(VERSION 2.8 FATAL_ERROR) set(CMAKE_LEGACY_CYGWIN_WIN32 0) project("tuto") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(warnings "-Wall -Wextra -Werror") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(warnings "/W4 /WX /EHsc") endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${warnings}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${warnings}") add_executable(tuto main.cpp) add_subdirectory(lib) target_link_libraries(tuto foo) target_link_libraries(tuto bar) set_property(TARGET tuto PROPERTY CXX_STANDARD 17) target_compile_features(tuto PRIVATE cxx_std_17)

from the ./lib/CmakeLists.txt file

add_subdirectory(Foo) add_subdirectory(Bar)

from the ./lib/Foo/CMakeLists.txt file

add_library(foo STATIC foo.cpp) target_include_directories(foo PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) set_property(TARGET foo PROPERTY CXX_STANDARD 17) target_compile_features(foo PRIVATE cxx_std_17)

from the ./lib/Bar/CMakeLists.txt file

add_library(bar STATIC bar.cpp) target_include_directories(bar PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) set_property(TARGET bar PROPERTY CXX_STANDARD 17) target_compile_features(bar PRIVATE cxx_std_17) target_link_libraries(bar foo)

Let’s start with the ./CMakeLists.txt

cmake_minimum_required(2.8 FATAL_ERROR) set(CMAKE_LEGACY_CYGWIN_WIN32 0) project("tuto")

This block sets the minimum version of Cmake usable. Ignores the warning of CYGWIN (if you use it does nothing otherwise) and defines the project.

if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(warnings "-Wall -Wextra -Werror") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(warnings "/W4 /WX /EHsc") endif()

This bloc adds warning during the compilation and threat warning as error. The if statement deals with the possibility of a different compiler. (for more info check GCC documentation and MSVC documentation)

add_executable(tuto main.cpp) add_subdirectory(lib) target_link_libraries(tuto foo) target_link_libraries(tuto bar) set_property(TARGET tuto PROPERTY CXX_STANDARD 17) target_compile_features(tuto PRIVATE cxx_std_17)

In this block, we create the executable. We add the subdirectory containing the libraries. We link the libraries. We add finally support for C++17.

Let’s now examine the CMakeList.txt in the lib folder

add_subdirectory(Foo) add_subdirectory(Bar)

Quite simple, isn’t it? We simply define the directory of the libraries used.

let’s check the ./lib/Foo/CMakeLists.txt file

add_library(foo STATIC foo.cpp) target_include_directories(foo PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) set_property(TARGET foo PROPERTY CXX_STANDARD 17) target_compile_features(foo PRIVATE cxx_std_17)

The add_library command defined a library from a .cpp file. This library can be used in other CMakeLists.txt files. A library can be STATIC SHARED or a MODULE. The documentation explains those properties quite well.

target_include_directories is used to set a directory containing the headers files. “PUBLIC” sets the scope of those header files. Again the documentation gives more detail.

let’s check the ./lib/Bar/CMakeLists.txt file

add_library(bar STATIC bar.cpp) target_include_directories(bar PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) set_property(TARGET bar PROPERTY CXX_STANDARD 17) target_compile_features(bar PRIVATE cxx_std_17) target_link_libraries(bar foo)

This file is very similar to the Foo files, but It showcases with the target_link_libraries(bar foo) that we can use the library create in any folder knew by CMake (of course the header file have to be public if we want to use it in our code).

To build the project you have to call inside the build folder(on Linux).

cmake -G "Unix Makefiles" .. make

Of course, you can automate a bit the process by making a .sh file (under Linux)

./make.sh mkdir build cd build cmake --build .

you can make it executable by calling

chmod +x ./make.sh

Reference

[1] John Lamp (2017), CMake Tutorial, https://www.johnlamp.net/category/cmake-tutorial

[2] Cmake (2019), Cmake, https://cmake.org/overview/