Ответ 1
Я ответил на свой вопрос с обрывами, которые я нашел через Интернет, и я собираюсь поделиться им здесь. Если кто-либо из вас знаком с CMake, пожалуйста, помогите мне исправить что-нибудь, о чем я могу ошибаться.
Что такое CMake для?
Согласно Википедии:
CMake - это [...] программное обеспечение для управления процессом сборки программного обеспечения используя независимый от компилятора метод. Он предназначен для поддержки иерархии каталогов и приложения, которые зависят от нескольких библиотеки. Он используется в сочетании с собственными средами сборки таких как make, Apple Xcode и Microsoft Visual Studio.
С помощью CMake вам больше не нужно поддерживать отдельные настройки, специфичные для вашей среды компилятора/сборки. У вас есть одна конфигурация, и это работает для многих сред. Cmake может создавать решение Microsoft Visual Studio, проект Eclipse или лабиринт Makefile из тех же файлов без изменения чего-либо в них.
Учитывая набор каталогов с кодом в них, CMake управляет всеми зависимостями, строит заказы и другие задачи, которые ваш проект должен выполнить, прежде чем он может быть скомпилирован. Он НЕ собирает ничего. Чтобы использовать CMake, вы должны сообщить ему (используя файлы конфигурации, называемые CMakeLists.txt), какие исполняемые файлы, которые вам нужно скомпилировать, какие библиотеки они ссылаются, какие каталоги находятся в вашем проекте и что внутри них, а также любые детали, такие как флаги или что-нибудь еще, что вам нужно (CMake достаточно мощный). Если это правильно настроено, вы затем используете CMake для создания всех файлов, которые ваша "собственная среда сборки" по своему выбору должна выполнять свою работу. В linux по умолчанию это означает Makefile. Поэтому, как только вы запустите 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
и выполняет это script, поэтому каждый добавленный таким образом подкаталог должен иметь файл 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
в подкаталогах, но вы должны определить ее, прежде чем использовать 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/
добавляется в каталоги 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
под lib/
и src/
и просто добавляя что-то вроде add_executable(Tutorial src/tutorial.cxx)
в 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
script.
Почему это важно? Исходные файлы, очевидно, не будут в дереве сборки. Если вы попробуете что-то вроде target_include_directories(Tutorial PUBLIC ../lib)
в предыдущем примере, этот путь будет относиться к дереву сборки, то есть он будет похож на запись ${CMAKE_BINARY_DIR}/lib
, которая будет выглядеть внутри simple/build/lib/
. Там нет файлов .h, в лучшем случае вы найдете libTestLib.a
. Вместо этого вы хотите ${CMAKE_SOURCE_DIR}/lib
.
-
CMAKE_CXX_FLAGS
: флаги для передачи компилятору, в данном случае компилятору С++. Также стоит отметить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/thenning, но 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 @[email protected]
#define Tutorial_VERSION_MINOR @[email protected]
Результирующий файл, сгенерированный CMake, simple/src/TutorialConfig.h
:
//configured options and settings
#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 1
С помощью умного использования вы можете делать такие классные вещи, как выключение библиотеки и т.д. Я рекомендую взглянуть на этот учебник, так как есть несколько более продвинутые вещи, которые рано или поздно будут очень полезны для крупных проектов.
Для всего остального переполнение стека наполняется конкретными вопросами и краткими ответами, что отлично подходит для всех, кроме непосвященных.