Title: [1.3] CMake
Course: Programowanie w języku C++ > Section: [1] Wprowadzenie

CMake to wieloplatformowe narzędzie do automatycznego zarządzania procesem kompilacji programu, niezależne od używanego kompilatora oraz platformy sprzętowej. CMake nie kompiluje programu samodzielnie, lecz tworzy pliki z regułami kompilacji dla konkretnego środowiska (np. pliki Makefile w systemie GNU/Linux).
Strona projektu: CMake
⚠️ Jeśli korzystasz z Linuxa lub Windowsa z kompilatorem MinGW, nie musisz dodatkowo instalować narzędzia CMake (Linux zawiera domyślnie instalację CMake, na Windowsie CMake zostanie zainstalowany w ramach MinGW).

Prosta konfiguracja projektu

Oto typowa zawartość pliku konfiguracji budowania CMakeLists.txt dla nowo utworzonego projektu w języku C:

cmake_minimum_required(VERSION 3.13)
# Zdefiniuj projekt o nazwie "dummy_project".
project(dummy_project)

# Domyślnie korzystaj ze standardu C11.
set(CMAKE_C_STANDARD 11)

# Zdefiniuj konfigurację budowania "dummy_project", która uwzględnia plik źródłowy `main.c`.
add_executable(dummy_project main.c)


Analogiczna typowa zawartość pliku konfiguracji budowania CMakeLists.txt dla nowo utworzonego projektu w języku C++:

cmake_minimum_required(VERSION 3.13)
# Zdefiniuj projekt o nazwie "dummy_project".
project(dummy_project)  # Nie ma potrzeby określania języka programowania.

# Domyślnie korzystaj ze standardu C++17.
set(CMAKE_CXX_STANDARD 17)

# Zdefiniuj konfigurację budowania "dummy_project", która uwzględnia plik źródłowy `main.cpp`.
add_executable(dummy_project main.cpp)


Pamiętaj, aby ustawić odpowiednie flagi kompilacji, dodając po instrukcji set(CMAKE_???_STANDARD ...) instrukcję add_compile_options() z żądanymi flagami, np.:

add_compile_options(-Wall -Wextra -Werror -Wpedantic -pedantic-errors -Wconversion)


W powyższym przykładzie użyte flagi odnoszą się do kompilatorów z pakietu GCC (oraz ich klonów, np. MinGW) – w przypadku innych kompilatorów flagi zapewne będą się różnić.
Nie dodawaj flagi -std=..., gdyż w CMake informacja o standardzie języka jest określana z użyciem odpowiednio set(CMAKE_C_STANDARD ...) dla języka C oraz set(CMAKE_CXX_STANDARD ...) dla języka C++.

Domyślnie CMake buduje projekt z użyciem języków C i C++ (zob. CMake: project - options, opcja LANGUAGES) i podczas kompilacji danego pliku źródłowego automatycznie wybiera właściwy kompilator, na podstawie rozszerzenia pliku źródłowego:
- .c – kompilator języka C
- .cpp – kompilator języka C++

Zmienne w CMake

Źródła: CMake − Variables, What's the CMake syntax to set and use variables? (Stack Overflow)

CMake umożliwia definiowanie zmiennych, co bardzo ułatwia parametryzację skryptu budowania programu:

set(my_var 1)  # Zdefiniuj zmienną `my_var` o wartości "1".
               #  ("1" oznacza łańcuch znaków - czyli tekst - a nie liczbę)


⚠️  Wszystkie zmienne w CMake są typu znakowego (ich wartością jest __zawsze__ łańcuch znaków). Aby odwołać się do wartości zmiennej (w typowym przypadku) użyj ${}, przykładowo:

set(my_var 1)  # Zdefiniuj zmienną `my_var` o wartości "1".
               #  ("1" oznacza łańcuch znaków - czyli tekst - a nie liczbę)
${my_var}  # Odwołaj się do wartości zmiennej `my_var`.


Oto przykład użycia zmiennej, aby zachować spójność między nazwą projektu, a nazwą konfiguracji budowania:

cmake_minimum_required(VERSION 3.13)
set(PROJECT_ID dummy_project)

project(${PROJECT_ID})

set(CMAKE_C_STANDARD 11)

add_executable(${PROJECT_ID}__debug main.c)


W efekcie zostanie utworzony projekt o nazwie "dummy_project" zawierający konfigurację budowania "dummy_project__debug".

Konfiguracja projektu złożonego z wielu plików (w tym nagłówkowych)

Zwykle w projektach lepiej jest trzymać pliki nagłówkowe w osobnym katalogu, o zwyczajowej nazwie include.
Aby kompilator uwzględniał ten katalog podczas procesu budowania aplikacji, umieść przed instrukcją add_executabe() instrukcję:

include_directories(include)


Pliki źródłowe zwykło umieszczać się w katalogu o zwyczajowej nazwie src.
Aby kompilator uwzględnił poszczególne pliki źródłowe podczas procesu budowania aplikacji, umieść w pliku CMakeLists.txt przed instrukcją add_executabe() poniższą instrukcję zawierającą względne ścieżki do plików źródłowych (podawane względem głównego katalogu projektu):

# Utwórz zmienną `SOURCE_FILES` przechowującą ścieżki do plików źródłowych.
set(SOURCE_FILES
        src/  # WZGLĘDNA ścieżka do pliku ``
        ...                    # ewentualne kolejne WZGLĘDNE ścieżki
        )


oraz zmień użycie instrukcji add_executabe() na poniższe:

# Uwzględnij zawartość zmiennej `SOURCE_FILES` w procesie kompilacji.
add_executable(dummy_project ${SOURCE_FILES} ...)  # ... oznacza inne pliki (np. main.c albo main.cpp)


Ostateczna wersja pliku konfiguracyjnego CMake dla przykładowego złożonego projektu (na przykładzie języka C++):
cmake_minimum_required(VERSION 3.13) # Zdefiniuj projekt o nazwie "dummy_project". project(dummy_project) # Korzystaj ze standardu C++17. set(CMAKE_CXX_STANDARD 17) # Ustaw flagi kompilacji. add_compile_options(-Wall -Wextra -Werror -Wpedantic -pedantic-errors) # Utwórz zmienną `SOURCE_FILES` przechowującą ścieżki do plików źródłowych. set(SOURCE_FILES src/source_1.cpp # ścieżka do pliku `source_1.cpp` src/source_2.cpp # ścieżka do pliku `source_2.cpp` ) # Uwzględnij zawartość zmiennej `SOURCE_FILES` w procesie kompilacji. add_executable(dummy_project ${SOURCE_FILES} main.cpp)