Mastering CMake (2)

Setting Up Your First CMake Project

cmake_minimum_required(VERSION 3.20)
project(Hello)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
add_executable(Hello hello.cpp)

project(<PROJECT-NAME>
        [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
        [DESCRIPTION <project-description-string>]
        [HOMEPAGE_URL <url-string>]
        [LANGUAGES <language-name>...])

cmake_minimum_required(VERSION 3.20.0)
project(Rental CXX)
add_executable(Rental
               main.cpp
               cars/car.cpp  
               # more files in other directories 
)

Working with Targets

Essentially, it’s a recipe that a buildsystem uses to compile a list of files into another file. It can be a .cpp implementation file compiled into an .o object file, a group of .o files packaged into an .a static library, and many other combinations.

All that’s required is an add_executable() command with the name of the executable target and a list of the files that are to be its elements

In CMake, we can create a target using one of three commands:

  • add_executable()
  • add_library()
  • add_custom_target()

add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            [<source>...])

add_executable(<name> [WIN32] [MACOSX_BUNDLE]
               [EXCLUDE_FROM_ALL]
               [source1] [source2 ...])

for build you can specify a target or build all project

--build binary_directroy --target [all|target]

add source to target

add_executable(main main.cpp)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
  target_sources(main PRIVATE gui_linux.cpp)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
  target_sources(main PRIVATE gui_windows.cpp)
endif()

preprocessing definitions

add paths to include

target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
  <INTERFACE|PUBLIC|PRIVATE> [item1...]
  [<INTERFACE|PUBLIC|PRIVATE> [item2...] ...])

add definition

set(VAR 8)
add_executable(defined definitions.cpp)
target_compile_definitions(defined PRIVATE ABC
  "DEF=${VAR}")
int main() {
#if defined(ABC)
    std::cout << "ABC is defined!" << std::endl;
#endif
#if (DEF < 2*4-3)
    std::cout << "DEF is greater than 5!" << std::endl;
#endif
}

compiler flags

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -DNO_DEBUG")
set(CMAKE_CXX_FLAGS "-Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")

Managing Dependencies with CMake

  • A static library has a .a extension on Unix-like systems and .lib on Windows.
  • Shared libraries have a .so extension on Unix-like systems and .dll on Windows.

if you install library like

apt install protobuf-compiler libprotobuf-dev

find_package(Protobuf REQUIRED)
target_link_libraries(main PRIVATE ${Protobuf_LIBRARIES})
target_include_directories(main PRIVATE 
  ${Protobuf_INCLUDE_DIRS}
  ${CMAKE_CURRENT_BINARY_DIR})

find_package(Protobuf REQUIRED) asks CMake to run the bundled FindProtobuf.cmake find-module and set up the Protobuf library for us. That find-module will scan commonly used paths and (because we provided the REQUIRED keyword) terminate if a library is not found. It will also specify useful variables and functions (such as the one on the next line).
target_link_libraries adds libraries (static or shared) found by find_package() to the linking command of our main target.

  • <PKG_NAME>_FOUND
  • <PKG_NAME>_INCLUDE_DIRS or <PKG_NAME>_INCLUDES
  • <PKG_NAME>_LIBRARIES or <PKG_NAME>_LIBRARIES or <PKG_NAME>_LIBS
  • <PKG_NAME>_DEFINITIONS
  • IMPORTED targets specified by the find-module or config-file

find_package(<Name> [version] [EXACT] [QUIET] [REQUIRED])

for cmake files have format <package>Config.cmake

find_package(Foo CONFIG REQUIRED)
target_link_libraries(boo foo)
add_library(MyLib SHARED ./lib/MyLib.cpp)
target_link_directories(MyLib PUBLIC ./lib)
target_include_directories(MyLib PUBLIC ./lib)
find_package(TBB REQUIRED)
target_link_libraries(MyLib PUBLIC tbb)

#libraries linked to MyLib target like tbb will used inside test target because it linked with MyLib and MyLib is public link with tbb

add_executable(test main.cpp ${res_files})
find_package(Qt6 REQUIRED COMPONENTS Core)
target_link_libraries(test PRIVATE Qt6::Core)
target_link_libraries(test  PUBLIC MyLib)