Как используется CMake? [закрыто]

95

Как известно, новичку сложно получить какую-либо полезную информацию о CMake. До сих пор я видел несколько руководств о том, как создать какой-нибудь очень простой проект или другой. Однако ни одно из них не объясняет причины, лежащие в основе чего-либо, что в них показано, всегда оставляя много дыр, которые нужно заполнить.

Что вызов CMake на через CMakeLists значит ? Он должен вызываться один раз для каждого дерева сборки или как? Как использовать разные настройки для каждой сборки, если все они используют один и тот же файл CMakeLists.txt из одного источника? Почему для каждого подкаталога нужен собственный файл CMakeLists? Имеет ли смысл использовать CMake в файле CMakeLists.txt, отличном от того, который находится в корне проекта? Если да, то в каких случаях? В чем разница между указанием того, как создать исполняемый файл или библиотеку из файла CMakeLists в их собственном подкаталоге, и указанием этого в файле CMakeLists в корне всех источников? Могу ли я сделать проект для Eclipse и другой для Visual Studio, просто изменив -Gпараметр при вызове CMake? Это вообще то, как это используется?

Ни одно из руководств, страниц документации или вопросов / ответов, которые я видел до сих пор, не дает полезного понимания того, как использовать CMake. Примеры просто не полны. Какие бы уроки я ни читал, мне кажется, что я упускаю что-то важное.

Новички в CMake, такие как я, задают много вопросов, которые не задают этого в явной форме, но делают очевидным тот факт, что мы, как новички, понятия не имеем, что делать с CMake или что с этим делать.

leinaD_natipaC
источник
8
Возможно, вам стоит вместо этого написать сообщение в блоге.
Стивир
2
У меня есть соблазн закрыть голосование «слишком широко» ... Это больше похоже на личное эссе по CMake, чем на подходящее для stackoverflow. Почему вы не разделили все эти вопросы на отдельные вопросы? И чем ваш вопрос + ответ отличается от многих других, например stackoverflow.com/q/20884380/417197 , stackoverflow.com/q/4745996/417197 , stackoverflow.com/q/4506193/417197 ?
André
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

Ответы:

139

Для чего нужен CMake?

Согласно Википедии:

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

С CMake вам больше не нужно поддерживать отдельные настройки, специфичные для вашей среды компиляции / сборки. У вас есть одна конфигурация, которая работает во многих средах.

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

Имея множество каталогов с кодом в них, CMake управляет всеми зависимостями, порядками сборки и другими задачами, которые необходимо выполнить вашему проекту перед его компиляцией. На самом деле он ничего НЕ компилирует. Чтобы использовать CMake, вы должны указать ему (используя файлы конфигурации, называемые CMakeLists.txt), какие исполняемые файлы вам нужно скомпилировать, с какими библиотеками они связаны, какие каталоги есть в вашем проекте и что внутри них, а также любые детали, такие как флаги. или что-нибудь еще, что вам нужно (CMake довольно мощный).

Если все настроено правильно, вы затем используете CMake для создания всех файлов, которые необходимы вашей «родной среде сборки» для выполнения своей работы. В Linux по умолчанию это Makefiles. Итак, как только вы запустите CMake, он создаст кучу файлов для собственного использования плюс несколько файлов Makefile. Все, что вам нужно делать после этого, это набирать "make" в консоли из корневой папки каждый раз, когда вы заканчиваете редактировать свой код, и бац, создается скомпилированный и связанный исполняемый файл.

Как работает 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описывает a, будет по умолчанию помещена в свой подкаталог в дереве сборки. Итак, после быстрого

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подкаталогов, но вы должны определить ее перед использованием, 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она не была бы найдена при компиляции Tutorial, поэтому вся lib/папка добавляется в каталоги include для поиска #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.txts под lib/и src/и просто добавив что-то вроде add_executable(Tutorial src/tutorial.cxx)to simple/CMakeLists.txt. Это зависит от вас и потребностей вашего проекта.

Что еще мне нужно знать, чтобы правильно использовать CMake?

(AKA темы, относящиеся к вашему пониманию)

Поиск и использование пакетов : ответ на этот вопрос объясняет это лучше, чем я когда-либо мог.

Объявление переменных и функций, использование потока управления и т . Д.: Ознакомьтесь с этим руководством, которое объясняет основы того, что может предложить CMake, а также является хорошим введением в целом.

Переменные CMake : их много, поэтому далее следует краткий курс, который направит вас на верный путь. Вики CMake - хорошее место для получения более подробной информации о переменных и, якобы, других вещах.

Вы можете изменить некоторые переменные, не перестраивая дерево сборки. Используйте для этого ccmake (он редактирует CMakeCache.txtфайл). Не забудьте выполнить configure, когда закончите с изменениями, а затем gустановите make-файлы с обновленной конфигурацией.

Прочтите ранее упомянутый учебник, чтобы узнать об использовании переменных, но короче: 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 изобилует конкретными вопросами и краткими ответами, что отлично подходит для всех, кроме непосвященных.

leinaD_natipaC
источник
2
Я принимаю этот ответ, но если кто-то другой напишет лучший, более короткий ответ, я выберу его. Я бы сделал это сам, но я уже достаточно пережил CMake.
leinaD_natipaC
6
Спасибо за эту замечательную работу. Надеюсь, это наберет больше голосов! :)
ДАВАЙТЕ
4
Здесь довольно заниженный момент: с CMake вам больше не нужно поддерживать отдельные настройки, специфичные для вашей среды компилятора / сборки. У вас есть одна конфигурация, которая работает для (различных версий) Visual Studio, блоков кода, простых файлов Unix Makefile и многих других сред. Вот почему я в первую очередь обратил внимание на CMake: главной проблемой стало поддерживать синхронизацию конфигураций Autoconf, MSVC 2005 / 32bit и MSVC 2010 / 64bit. И как только я освоился, я добавил еще много вещей, таких как создание документов, тестирование, упаковка и т. Д., И все это кроссплатформенным способом.
DevSolar
1
@DevSolar Правда. Мне эта строка нравится больше, чем в Википедии, поэтому я добавлю ее к ответу, если вы не возражаете. Однако я не буду вдаваться в подробности о том, как правильно использовать CMake для этого. Это больше просто возможность сказать, что происходит.
leinaD_natipaC
1
@RichieHH Следуя примеру, вы сначала настраиваете CMake для использования Makefiles, затем вы используете CMake для генерации файлов, которые необходимо построить для вашего проекта, и, наконец, вы перестаете беспокоиться о CMake, потому что его работа здесь выполнена. Итак, вы «беспокоитесь» о Makefile в том смысле, что с этого момента вам нужно использовать его makeв bash для создания своего проекта.
leinaD_natipaC