Для чого призначений CMake?
За даними Вікіпедії:
CMake - це [...] програмне забезпечення для управління процесом побудови програмного забезпечення за допомогою незалежного від компілятора методу. Він призначений для підтримки ієрархій каталогів та програм, які залежать від декількох бібліотек. Він використовується у поєднанні з власними середовищами збірки, такими як make, Xcode від Apple та Microsoft Visual Studio.
За допомогою CMake вам більше не потрібно підтримувати окремі налаштування, характерні для вашого компілятора / середовища збірки. У вас одна конфігурація, яка працює в багатьох середовищах.
CMake може генерувати рішення Microsoft Visual Studio, проект Eclipse або лабіринт Makefile з тих самих файлів, не змінюючи нічого в них.
З огляду на купу каталогів із кодом у них, CMake управляє усіма залежностями, побудовою замовлень та іншими завданнями, які потрібно виконати перед проектом, перш ніж його можна буде скомпілювати. Він насправді нічого не компілює. Щоб використовувати CMake, ви повинні повідомити йому (використовуючи файли конфігурації, що називаються CMakeLists.txt), які виконувані файли вам потрібно скомпілювати, до яких бібліотек вони посилаються, які каталоги є у вашому проекті та що знаходиться всередині них, а також будь-які деталі, такі як прапори або що-небудь ще, що вам потрібно (CMake досить потужний).
Якщо це правильно налаштовано, ви використовуєте CMake для створення всіх файлів, які ваше "рідне середовище збірки", яке ви вибрали, має виконувати свою роботу. У Linux за замовчуванням це означає Makefiles. Отже, як тільки ви запустите CMake, він створить купу файлів для власного використання плюс кілька Makefile
s. Потім потрібно лише ввести "make" у консолі з кореневої папки кожного разу, коли закінчите редагувати свій код, і bam, буде створено скомпільований та пов'язаний виконуваний файл.
Як працює CMake? Що це робить?
Ось приклад налаштування проекту, який я буду використовувати протягом усього:
simple/
CMakeLists.txt
src/
tutorial.cxx
CMakeLists.txt
lib/
TestLib.cxx
TestLib.h
CMakeLists.txt
build/
Вміст кожного файлу відображається та обговорюється надалі.
CMake налаштовує ваш проект відповідно до кореня CMakeLists.txt
проекту і робить це в будь-якому каталозі, який ви виконували cmake
на консолі. Виконання цього з папки , яка не є коренем вашого проекту виробляє то , що називається поза джерела збірки, що означає файли , створені під час компіляції (OBJ файли, Lib файли, що виконуються файли , ви знаєте) , будуть розміщені в зазначеній папці , зберігається окремо від фактичного коду. Це допомагає зменшити безлад і є кращим з інших причин, які я не буду обговорювати.
Я не знаю, що трапиться, якщо ви виконаєте cmake
будь-яку іншу операцію, окрім кореневої CMakeLists.txt
.
У цьому прикладі, оскільки я хочу, щоб все це було розміщено всередині build/
папки, спочатку я маю перейти туди, а потім передати CMake каталог, в якому знаходиться кореневий каталог CMakeLists.txt
.
cd build
cmake ..
За замовчуванням це налаштовує все, використовуючи Makefiles, як я вже говорив. Ось як зараз повинна виглядати папка складання:
simple/build/
CMakeCache.txt
cmake_install.cmake
Makefile
CMakeFiles/
(...)
src/
CMakeFiles/
(...)
cmake_install.cmake
Makefile
lib/
CMakeFiles/
(...)
cmake_install.cmake
Makefile
Що це за всі ці файли? Єдине, про що вам слід турбуватися, це Makefile та папки проекту .
Зверніть увагу , що src/
і lib/
папку. Вони були створені, оскільки simple/CMakeLists.txt
вказує на них за допомогою команди add_subdirectory(<folder>)
. Ця команда вказує CMake шукати у вказаній папці інший CMakeLists.txt
файл і виконувати цей сценарій, тому кожен підкаталог, доданий таким чином, повинен мати CMakeLists.txt
файл всередині. У цьому проекті simple/src/CMakeLists.txt
описано, як створити власне виконуваний файл, та simple/lib/CMakeLists.txt
описано, як побудувати бібліотеку. Кожна ціль, яку CMakeLists.txt
описує, буде розміщена за замовчуванням у своєму підкаталозі в дереві побудови. Отже, після швидкого
make
у консоль, зроблену з build/
, додано кілька файлів:
simple/build/
(...)
lib/
libTestLib.a
(...)
src/
Tutorial
(...)
Проект побудований, і виконуваний файл готовий до виконання. Що ви робите, якщо хочете, щоб виконувані файли були поміщені в певну папку? Встановіть відповідну змінну CMake або змініть властивості певної цілі . Детальніше про змінні CMake пізніше.
Як я можу сказати CMake, як будувати свій проект?
Ось пояснюваний вміст кожного файлу у вихідному каталозі:
simple/CMakeLists.txt
:
cmake_minimum_required(VERSION 2.6)
project(Tutorial)
# Add all subdirectories in this project
add_subdirectory(lib)
add_subdirectory(src)
Завжди слід встановлювати мінімально необхідну версію, відповідно до попередження, яке CMake видає, коли ви цього не робите. Використовуйте будь-яку вашу версію CMake.
Ім'я вашого проекту можна використовувати пізніше, і це натякає на те, що ви можете керувати декількома проектами з тих самих файлів CMake. Однак я не буду заглиблюватися в це.
Як вже згадувалося раніше, add_subdirectory()
додає папку до проекту, а це означає, що CMake очікує CMakeLists.txt
, що в ній буде внутрішня частина , яку він потім запустить перед продовженням. До речі, якщо у вас випадково визначена функція CMake, ви можете використовувати її з інших CMakeLists.txt
s у підкаталогах, але вам доведеться визначити її перед використанням, add_subdirectory()
інакше вона її не знайде. Однак CMake розумніший щодо бібліотек, тому, мабуть, це єдиний раз, коли ви зіткнетеся з такою проблемою.
simple/lib/CMakeLists.txt
:
add_library(TestLib TestLib.cxx)
Щоб створити власну бібліотеку, ви даєте їй назву, а потім перелічуєте всі файли, з яких вона побудована. Прямий. Якщо йому потрібен інший файл foo.cxx
,, для компіляції ви замість цього пишете add_library(TestLib TestLib.cxx foo.cxx)
. Наприклад, це також працює для файлів в інших каталогах add_library(TestLib TestLib.cxx ${CMAKE_SOURCE_DIR}/foo.cxx)
. Детальніше про змінну CMAKE_SOURCE_DIR пізніше.
Ще одне, що ви можете зробити з цим, - вказати, що вам потрібна спільна бібліотека. Приклад: add_library(TestLib SHARED TestLib.cxx)
. Не бійтеся, саме тут CMake починає полегшувати ваше життя. Незалежно від того, чи є він спільним чи ні, тепер для використання бібліотеки, створеної таким чином, вам потрібно обробити лише ім’я, яке ви дали йому тут. Тепер ця бібліотека називається TestLib, і ви можете посилатися на неї з будь-якої точки проекту. CMake знайде його.
Чи є кращий спосіб перерахувати залежності? Безумовно, так . Докладніше про це див. Нижче.
simple/lib/TestLib.cxx
:
#include <stdio.h>
void test() {
printf("testing...\n");
}
simple/lib/TestLib.h
:
#ifndef TestLib
#define TestLib
void test();
#endif
simple/src/CMakeLists.txt
:
# Name the executable and all resources it depends on directly
add_executable(Tutorial tutorial.cxx)
# Link to needed libraries
target_link_libraries(Tutorial TestLib)
# Tell CMake where to look for the .h files
target_include_directories(Tutorial PUBLIC ${CMAKE_SOURCE_DIR}/lib)
Команда add_executable()
працює точно так само, як add_library()
, за винятком того, що, звичайно, вона згенерує виконуваний файл. На цей виконуваний файл тепер можна посилатися як на ціль для таких речей, як target_link_libraries()
. Оскільки tutorial.cxx використовує код, знайдений у бібліотеці TestLib, ви вказуєте на це CMake, як показано.
Подібним чином, будь-які .h-файли, # add_executable()
включені будь-якими джерелами , які не знаходяться в тому ж каталозі, що і джерело, повинні бути якимось чином додані. Якби не target_include_directories()
команда, lib/TestLib.h
її не було б знайдено під час компіляції підручника, тому вся lib/
папка додається до каталогів включення для пошуку #includes. Ви також можете побачити команду, include_directories()
яка діє подібним чином, за винятком того, що вам не потрібно вказувати ціль, оскільки вона прямо встановлює її глобально для всіх виконуваних файлів. Ще раз поясню CMAKE_SOURCE_DIR пізніше.
simple/src/tutorial.cxx
:
#include <stdio.h>
#include "TestLib.h"
int main (int argc, char *argv[])
{
test();
fprintf(stdout, "Main\n");
return 0;
}
Зверніть увагу, як включений файл "TestLib.h". Не потрібно включати повний шлях: CMake піклується про все те, що за кадром завдяки target_include_directories()
.
З технічної точки зору, в вихідному дереві просто , як це можна зробити без CMakeLists.txt
з під lib/
і src/
і просто додати що - щось на зразок add_executable(Tutorial src/tutorial.cxx)
до simple/CMakeLists.txt
. Це залежить від вас та потреб вашого проекту.
Що ще я повинен знати, щоб правильно використовувати CMake?
(AKA теми, що стосуються вашого розуміння)
Пошук та використання пакетів : Відповідь на це запитання пояснює це краще, ніж я коли-небудь міг.
Оголошення змінних та функцій, використання потоку керування тощо : ознайомтеся з цим посібником, який пояснює основи того, що може запропонувати CMake, а також як хороший вступ загалом.
Змінних CMake : їх багато, тому далі йде аварійний курс, який допоможе вам вийти на правильний шлях. Вікі CMake - це гарне місце, щоб отримати більш глибоку інформацію про змінні, а також нібито й про інші речі.
Можливо, вам доведеться відредагувати деякі змінні без відновлення дерева збірки. Для цього використовуйте ccmake (він редагує CMakeCache.txt
файл). Не забудьте набрати малюнок, c
коли закінчите зі змінами, а потім g
створити файли з оновленою конфігурацією.
Прочитайте підручник, на який раніше посилались, щоб дізнатись про використання змінних, але коротше:
set(<variable name> value)
змінити або створити змінну.
${<variable name>}
використовувати його.
CMAKE_SOURCE_DIR
: Кореневий каталог джерела. У попередньому прикладі це завжди дорівнює/simple
CMAKE_BINARY_DIR
: Кореневий каталог збірки. У попередньому прикладі це дорівнює simple/build/
, але якщо ви запускали cmake simple/
з такої папки, як foo/bar/etc/
, тоді всі посилання на CMAKE_BINARY_DIR
це дерево побудови стануть /foo/bar/etc
.
CMAKE_CURRENT_SOURCE_DIR
: Каталог, в якому знаходиться поточний CMakeLists.txt
файл. Це означає, що він змінюється протягом усього часу: друк цього з simple/CMakeLists.txt
доходів /simple
та друк з simple/src/CMakeLists.txt
доходів /simple/src
.
CMAKE_CURRENT_BINARY_DIR
: Ви зрозуміли ідею. Цей шлях залежатиме не тільки від папки, в якій знаходиться збірка, але й від поточного CMakeLists.txt
розташування сценарію.
Чому це важливо? Вихідних файлів, очевидно, не буде в дереві збірки. Якщо ви спробуєте щось подібне target_include_directories(Tutorial PUBLIC ../lib)
до попереднього прикладу, цей шлях буде відносно дерева збірки, тобто це буде як написання ${CMAKE_BINARY_DIR}/lib
, яке загляне всередину simple/build/lib/
. Там немає файлів .h; щонайбільше ви знайдете libTestLib.a
. Ви хочете ${CMAKE_SOURCE_DIR}/lib
замість цього.
CMAKE_CXX_FLAGS
: Прапори для передачі компілятору, в даному випадку компілятору C ++. Також варто зазначити, CMAKE_CXX_FLAGS_DEBUG
що замість цього використовуватиметься, якщо CMAKE_BUILD_TYPE
встановлено DEBUG. Є більше подібних; перегляньте вікі CMake .
CMAKE_RUNTIME_OUTPUT_DIRECTORY
: Скажіть CMake, куди слід помістити всі виконувані файли при побудові. Це глобальна обстановка. Наприклад, ви можете встановити для нього bin/
і розмістити там все акуратно. EXECUTABLE_OUTPUT_PATH
подібний, але застарілий, на випадок, якщо ви натрапите на нього.
CMAKE_LIBRARY_OUTPUT_DIRECTORY
: Аналогічним чином, глобальний параметр повідомляти CMake, куди зберігати всі файли бібліотеки.
Властивості цілі : ви можете встановити властивості, які впливають лише на одну ціль, будь то виконуваний файл або бібліотека (або архів ... ви розумієте). Ось хороший приклад того, як ним користуватися (з set_target_properties()
.
Чи існує простий спосіб автоматичного додавання джерел до цілі автоматично? Використовуйте GLOB, щоб перерахувати все в даному каталозі під тією ж змінною. Прикладом синтаксису є FILE(GLOB <variable name> <directory>/*.cxx)
.
Чи можете ви вказати різні типи збірки? Так, хоча я не впевнений у тому, як це працює, або в обмеженнях цього. Ймовірно, для цього потрібно щось if / then'ning, але CMake пропонує деяку базову підтримку, не налаштовуючи нічого, наприклад, за замовчуванням для CMAKE_CXX_FLAGS_DEBUG
, наприклад. Ви можете встановити тип збірки всередині CMakeLists.txt
файлу за допомогою set(CMAKE_BUILD_TYPE <type>)
або, зателефонувавши CMake з консолі з відповідними прапорцями, наприклад cmake -DCMAKE_BUILD_TYPE=Debug
.
Хороші приклади проектів, які використовують CMake? У Вікіпедії є список проектів з відкритим кодом, які використовують CMake, якщо ви хочете це вивчити. Інтернет-підручники для мене поки що були лише розчаруванням у цьому відношенні, однак це запитання щодо переповнення стека має досить прохолодну та зрозумілу установку CMake. Варто подивитися.
Використання змінних з CMake у коді : Ось швидкий і брудний приклад (адаптований з іншого підручника ):
simple/CMakeLists.txt
:
project (Tutorial)
# Setting variables
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 1)
# Configure_file(<input> <output>)
# Copies a file <input> to file <output> and substitutes variable values referenced in the file content.
# So you can pass some CMake variables to the source code (in this case version numbers)
configure_file (
"${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
"${PROJECT_SOURCE_DIR}/src/TutorialConfig.h"
)
simple/TutorialConfig.h.in
:
// Configured options and settings
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
Отриманий файл , згенерований CMake, simple/src/TutorialConfig.h
:
// Configured options and settings
#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 1
Завдяки розумному їх використанню ви можете робити такі цікаві речі, як вимкнення бібліотеки тощо. Я рекомендую поглянути на цей підручник, оскільки є деякі дещо вдосконаленіші речі, які рано чи пізно можуть бути дуже корисними для великих проектів.
До всього іншого, Stack Overflow наповнений конкретними питаннями та короткими відповідями, що чудово підходить для всіх, крім непосвячених.