Як використовується CMake? [зачинено]


95

Як відомо, важко отримати будь-яку корисну інформацію про CMake як початківцю. Поки що я бачив кілька підручників про те, як створити якийсь дуже базовий проект чи інший. Однак жодне з них не пояснює міркувань нічого, що в них показано, завжди залишаючи багато дірок для заповнення.

Що виклик CMake на через CMakeLists значить ? Це повинно називатися один раз на дерево побудови чи що? Як я можу використовувати різні налаштування для кожної збірки, якщо всі вони використовують один і той же файл CMakeLists.txt з одного джерела? Чому кожному підкаталогу потрібен власний файл CMakeLists? Чи було б сенсом використовувати CMake у CMakeLists.txt, відмінному від того, що знаходиться в корені проекту? Якщо так, то в яких випадках? Яка різниця між тим, як вказати, як створити виконуваний файл або бібліотеку з файлу CMakeLists у їх власному підкаталозі, від того, як це робити у файлі CMakeLists у кореневій частині всіх джерел? Чи можу я створити проект для Eclipse та інший для Visual Studio, просто змінивши -Gпараметр під час виклику CMake? Це навіть так використовується?

Жоден з підручників, сторінок документації чи запитань / відповідей, які я бачив до цього часу, не дає жодної корисної інформації щодо розуміння того, як користуватися CMake. Приклади просто не ґрунтовні. Незалежно від того, які підручники я читаю, я відчуваю, що пропускаю щось важливе.

Є багато питань, які задають такі новачки CMake, як я, які не задають цього явно, але роблять очевидним той факт, що ми, як новачки, не маємо уявлення, як мати справу з CMake або що з цим робити.


8
Можливо, замість цього вам слід написати допис у блозі.
steveire

2
Я маю спокусу закрити голосування "занадто широким" ... Це більше схоже на особисте есе про CMake, ніж на придатність для stackoverflow. Чому ви не поставили всі ці питання в окремі питання? І чим ваше запитання + відповідь відрізняється від багатьох інших, наприклад, stackoverflow.com/q/20884380/417197 , stackoverflow.com/q/4745996/417197 , stackoverflow.com/q/4506193/417197 ?
Андре

13
@Andre Чому це питання: Я тиждень намагався зрозуміти CMake, і жоден підручник, який я знайшов, не був повним. Ці посилання, на які ви вказали, хороші, але для тих, хто знайшов CMake, тому що їм накинули проект із купою CMakeLists всередині, вони не проливають багато світла на те, як працює CMake. Я, чесно кажучи, намагався написати відповідь, яку мені хотілося б надіслати назад у часі собі, коли я почав намагатися вивчити CMake. І підзапитання мають на меті показати, що я насправді намагаюся запитати, що саме я повинен знати, щоб не мати сумнівів. Правильно поставити запитання - ТВЕРДО.
leinaD_natipaC

2
Не відповідь, але я запрошую вас поглянути на jaws.rootdirectory.de . У той час як документація CMake (і прийнята відповідь ...) показує вам багато фрагментів того, що ви можете зробити, розміром із укус , я написав (аж ніяк не "повне" чи "ідеальне") базове налаштування, включаючи коментарі, що IMHO демонструє, що CMake (і CTest, і CPack, і CDash ...) можуть зробити для вас. Він створюється нестандартно, і ви можете легко адаптувати / розширити його відповідно до потреб вашого проекту.
DevSolar

9
Шкода, що сьогодні такі питання закриваються. Я думаю, що загальні запитання, що вимагають підручників, як відповіді, але стосуються погано / неясно задокументованих тем (іншим прикладом може бути Torch7 C api), а запитання на рівні дослідження повинні залишатися відкритими. Це набагато цікавіше, ніж типове "привіт, я маю сегмент, коли роблю якийсь іграшковий проект", який мучить сайт.
Ash

Відповіді:


137

Для чого призначений CMake?

За даними Вікіпедії:

CMake - це [...] програмне забезпечення для управління процесом побудови програмного забезпечення за допомогою незалежного від компілятора методу. Він призначений для підтримки ієрархій каталогів та програм, які залежать від декількох бібліотек. Він використовується у поєднанні з власними середовищами збірки, такими як make, Xcode від Apple та Microsoft Visual Studio.

За допомогою CMake вам більше не потрібно підтримувати окремі налаштування, характерні для вашого компілятора / середовища збірки. У вас одна конфігурація, яка працює в багатьох середовищах.

CMake може генерувати рішення Microsoft Visual Studio, проект Eclipse або лабіринт Makefile з тих самих файлів, не змінюючи нічого в них.

З огляду на купу каталогів із кодом у них, CMake управляє усіма залежностями, побудовою замовлень та іншими завданнями, які потрібно виконати перед проектом, перш ніж його можна буде скомпілювати. Він насправді нічого не компілює. Щоб використовувати CMake, ви повинні повідомити йому (використовуючи файли конфігурації, що називаються CMakeLists.txt), які виконувані файли вам потрібно скомпілювати, до яких бібліотек вони посилаються, які каталоги є у вашому проекті та що знаходиться всередині них, а також будь-які деталі, такі як прапори або що-небудь ще, що вам потрібно (CMake досить потужний).

Якщо це правильно налаштовано, ви використовуєте CMake для створення всіх файлів, які ваше "рідне середовище збірки", яке ви вибрали, має виконувати свою роботу. У Linux за замовчуванням це означає Makefiles. Отже, як тільки ви запустите CMake, він створить купу файлів для власного використання плюс кілька Makefiles. Потім потрібно лише ввести "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.txts у підкаталогах, але вам доведеться визначити її перед використанням, 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 наповнений конкретними питаннями та короткими відповідями, що чудово підходить для всіх, крім непосвячених.


2
Я приймаю цю відповідь, але якщо хтось інший напише кращу, коротшу відповідь, я виберу її. Я б зробив це сам, але я вже достатньо постраждав від CMake.
leinaD_natipaC

6
Дякую за цю дивовижну роботу. Сподіваюся, це набере більше голосів! :)
LETs

4
Досить занижений момент: за допомогою CMake вам більше не потрібно підтримувати окремі налаштування, характерні для вашого компілятора / середовища збірки. У вас є одна конфігурація, яка працює для (різних версій) Visual Studio, блоків коду, простих Unix Makefiles та багатьох інших середовищ. Саме тому я передусім дивився на CMake: стало серйозною проблемою синхронізація конфігурацій Autoconf, MSVC 2005 / 32bit та MSVC 2010 / 64bit. І коли я зрозумів це, я додав ще багато речей, таких як генерація документів, тестування, упаковка тощо, і все це міжплатформеним способом.
DevSolar

1
@DevSolar True. Мені цей рядок подобається більше, ніж вікіпедії, тому я додаю його до відповіді, якщо ви не проти. Однак я нічого не вміщу в тому, як правильно використовувати CMake з цією метою. Це більше стосується просто можливості сказати, що відбувається.
leinaD_natipaC

1
@RichieHH Наслідуючи приклад, ви спочатку налаштовуєте CMake на використання Makefiles, потім ви використовуєте CMake для генерації файлів, які потрібно створити для вашого проекту, і нарешті ви перестаєте турбуватися про CMake, оскільки його робота тут виконана. Отже, ви "турбуєтеся" про Makefile в тому сенсі, що відтепер вам потрібно використовувати makebash, щоб, ну, зробити свій проект.
leinaD_natipaC
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.