Для чего нужен 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.txt
s под lib/
и src/
и просто добавив что-то вроде add_executable(Tutorial src/tutorial.cxx)
to simple/CMakeLists.txt
. Это зависит от вас и потребностей вашего проекта.
Что еще мне нужно знать, чтобы правильно использовать CMake?
(AKA темы, относящиеся к вашему пониманию)
Поиск и использование пакетов : ответ на этот вопрос объясняет это лучше, чем я когда-либо мог.
Объявление переменных и функций, использование потока управления и т . Д.: Ознакомьтесь с этим руководством, которое объясняет основы того, что может предложить CMake, а также является хорошим введением в целом.
Переменные CMake : их много, поэтому далее следует краткий курс, который направит вас на верный путь. Вики CMake - хорошее место для получения более подробной информации о переменных и, якобы, других вещах.
Вы можете изменить некоторые переменные, не перестраивая дерево сборки. Используйте для этого ccmake (он редактирует CMakeCache.txt
файл). Не забудьте выполнить c
onfigure, когда закончите с изменениями, а затем 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 изобилует конкретными вопросами и краткими ответами, что отлично подходит для всех, кроме непосвященных.