СMake
CMake значительно упрощает процесс сборки C++ проектов, состоящих из многих файлов. У этой системы есть много параметров, которые описаны в документации. На этой странице я предлагаю познакомиться с CMake на практике на примере простой задачи.
Базовый вариант
Мы написали простую программу в файле main.cpp
:
// main.cpp
#include <iostream>
unsigned long long factorial(unsigned long long n){
return n == 0 ? 1ull : n * factorial(n - 1);
}
int main() {
std::cout << factorial(5) << std::endl;
return 0;
}
Теперь нам нужно её скомпилировать. Мы могли бы вручную вызвать компилятор и прописать ему все необходимые флаги. Получилось бы подобная команда (для release сборки):
g++ main.cpp -o example.exe -O3 -DNDEBUG -std=c++20
Но каждый раз прописывать все флаги немного утомительно. А также, если у нас в проекте есть несколько .cpp
файлов, то команда станет ещё намного страшнее. Поэтому попробуем использовать CMake, где мы пропишем всё необходимое 1 раз.
Для работы с CMake необходимо создать конфигурационный файл CMakeLists.txt
в корне проекта. Для нешей задачи его можно заполнить следующим образом:
# Проверка версии CMake.
cmake_minimum_required(VERSION 3.2)
# Если версия установленой программы
# старее указаной, произайдёт аварийный выход.
project(ExampleProg LANGUAGES CXX)
# Название проекта
# и указание используемых языков
# какой компилятор использовать к какому файлу
# будет определяться по расширению можно добавить
# "C" -- си, "FORTRAN" -- фортран, "CUDA" -- cuda
set(CMAKE_CXX_STANDARD 20) # Установка стандарта c++
set(SOURCE_EXE main.cpp)
# Установка переменной со списком исходников
# (может быть несколько файлов)
add_executable(Example ${SOURCE_EXE})
# Создает исполняемый файл с именем Example
# из всех исходников в ${SOURCE_EXE}
Сборка
После этого нам остаётся собрать проект:
mkdir build # Создать папку build
cd build # Зайти в папку build
cmake -G "MinGW Makefiles" .. # Подготовить файлы для сборки
cmake --build . # Запустить сборку
Флаг -G
нужно писать, когда у вас несколько компиляторов (например, от Visual Studio и MinGW).
Можно делегировать создание папки самому CMake с помощью флагов -S
и -B
, которые указывают на директорию с CMakeLists.txt и директорию для сборки. Также можно прописать тип сборки указав значение CMAKE_BUILD_TYPE
. Это можно было прописать в конфигруационный файл, но тогда мы бы не имели возможность выбора. В итоге для сборки нужно прописать
- в случае Release сборки:
cmake -S . -B build/ -D CMAKE_BUILD_TYPE=Release -G "MinGW Makefiles"
cmake --build build/
- в случае Debug сборки:
cmake -S . -B build/ -D CMAKE_BUILD_TYPE=Debug -G "MinGW Makefiles"
cmake --build build/
Вариант с библиотекой
Теперь попробуем вынести factorial
в библиотеку.
Теперь у нас структура проекта:
-- main.cpp
-- CMakeLists.txt
-- factorial
. . . .| -- CMakeLists.txt
. . . .| -- factorial.h
. . . .| -- factorial.cpp
// main.cpp
#include <iostream>
#include <factorial.h>
int main() {
std::cout << factorial(5) << std::endl;
return 0;
}
Обратите внимание на строку #include <factorial.h>
. В обычной ситуации мы бы использовали #include "factorial/factorial.h"
, но CMake позволит нам не писать полные пути до файлов.
// factorial.h
unsigned long long factorial(unsigned long long);
// factorial.cpp
#include "factorial.h"
unsigned long long factorial(unsigned long long n){
return n == 0 ? 1ull : n * factorial(n - 1);
}
Дополним исходный CMakeLists.txt
:
cmake_minimum_required(VERSION 3.2)
project(ExampleProg LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(SOURCE_EXE main.cpp)
include_directories(./factorial)
# Расположение заголовочных файлов
# (будет доступно всем target'ам проекта)
# Альтернатива: target_include_directories
add_executable(Example ${SOURCE_EXE})
add_subdirectory(./factorial)
# Добавление подпроекта, указывается имя дирректории
# в этой директории должен быть свой небольшой CMakeLists.txt
# Линковка программы с библиотеками
target_link_libraries(Example Factorial)
# после названия цели (Example) идёт перечисление библиотек
И добавим CMakeLists.txt
в директорию factorial
:
cmake_minimum_required(VERSION 3.2)
project(factorial)
set(SOURCE_LIB factorial.cpp)
# Создание статической библиотеки
add_library(Factorial STATIC ${SOURCE_LIB})
# если написать SHARED, то будет динамическая
Подпроекты получат глобальные параметры (используемые языки, доп.библиотеки, настройки компиляторов и т.п.) из основного CMakeLists.txt
.
Проект готов к сборке.