Полное руководство по CMake. Часть первая: Синтаксис +40
Из песочницы, C++, Системы сборки, C
Рекомендация: подборка платных и бесплатных курсов 3D-моделирования — https://katalog-kursov.ru/
Введение
CMake — это открытый и кросс-платформенный набор утилит, предназначенных для автоматизации тестирования, компиляции и создания пакетов проектов на C/C++. Написав однажды небольшой и понятный всем скрипт, Вы тем самым обеспечите одинаковую сборку Вашего проекта на любых платформах, где доступен CMake.
Язык CMake, будучи транслированным в нативный файл сборки (например, Makefile или Ninja), определяет процесс всего управления проектом. В Вашем распоряжении, с функциональной стороны, есть лишь команды, которые могут образовываться в довольно сложные конструкции. С них мы и начнём.
Ниже приведены примеры использования языка CMake, по которым Вам следует попрактиковаться. Экспериментируйте с исходным кодом, меняя существующие команды и добавляя новые. Чтобы запустить данные примеры, следуйте этим шагам:
- Установите программу CMake с официального сайта
- Создайте на рабочем столе текстовый файл
CMakeLists.txt
- Добавьте в начало файла
cmake_minimum_required(VERSION 3.0)
- Скопируйте туда исходные тексты необходимых примеров
- Если у Вас установлен консольный CMake, то запустить скрипт можно с помощью команды «
cmake .
«. Если у Вас графический CMake, то в первые два верхних поля приложения вбейте адрес Вашего рабочего стола, затем нажмите кнопкуGenerate
. Результат появится в нижнем текстовом поле.
Команды
Команды в CMake подобны функциям во многих языках программирования. Чтобы вызвать команду, необходимо написать её имя, а затем передать ей обрамлённые в круглые скобки аргументы, отделённые символами пробелов. В приведённом примере команде message
передаются шесть аргументов для вывода в консоль:
# Напечатает в консоль "CMake is the most powerful buildsystem!"
message("CMake " "is " "the " "most " "powerful " "buildsystem!")
Аргументы
Аргументы, обрамлённые в двойные кавычки, позволяют внутри себя совершать экранирование и подстановку переменных. Необрамлённые аргументы не позволяют производить подобных вещей и не могут включать в себя символы ()#"
и пробелы, однако более удобны для использования. Пример:
# Напечатает "Hello, my lovely CMake", один таб и "!":
message("Hello, my lovely CMaket!")
# Напечатает "Hello,_my_lovely_CMake!" без пробелов:
message(Hello,_my_lovely_CMake!)
Стоит отметить, что аргумент Walk;around;the;forest
расширится до списка Walk around the forest
, так как любой необрамлённый аргумент автоматически расширяется до списка значений (при условии, что значения изначального аргумента разделены символами точки с запятой), но с обрамлённым в двойные кавычки аргументом такая трансформация не происходит (символы точки с запятой просто исчезают). Об этой особенности упомянули в комментариях.
Комментарии
Комментарии начинаются с символа решётки и заканчиваются в конце той строки, где они были напечатаны. Текст, заключённый в комментариях, игнорируется системой сборки и не оказывает никакого эффекта на её работу. Примеры выше также демонстрируют использование комментариев.
Переменные
Переменные можно определить путём вызова команды set
, а удалить вызовом unset
. Получить значение переменной можно по конструкции ${VARIABLE}
. Если переменная ещё не определена и где-то потребовалось получить её значение, то данная переменная обратится в пустую строку. Пример:
# Определить переменную VARIABLE со значением "Mr. Thomas":
set(VARIABLE "Mr. Thomas")
# Напечает "His name is: Mr. Thomas":
message("His name is: " ${VARIABLE})
# Напечатает "'BINGO' is equal to: []", так как "BINGO" не определена:
message("'BINGO' is equal to: [${BINGO}]")
# Удалить переменную VARIABLE:
unset(VARIABLE)
Логические выражения
Прежде чем приступать к изучению условных операторов и циклических конструкций, необходимо понимать работу логических выражений. Логические выражения используются при проверки условий и могут принимать одно из двух значений: правда или ложь. Например, выражение 52 LESS 58
обратится в правду, так как 52 < 58. Выражение 88 EQUAL 88
обратится в правду, 63 GREATER 104
обратится в ложь. Сравнивать можно не только числа, но и строки, версии, файлы, принадлежность к списку и регулярные выражения. Полный список логических выражений можно посмотреть тут.
Условные операторы
Условные операторы в CMake работают в точности как в других языках программирования. В данном примере сработает лишь первый условный оператор, который проверяет, что 5 > 1. Второе и третье условия ложны, так как 5 не может быть меньше или равняться одному. Блоки команд elseif(5 LESS 1)
и else()
необязательны, а endif()
обязательна и сигнализирует о завершении предыдущих проверок.
# Напечатает "Of course, 5 > 1!":
if(5 GREATER 1)
message("Of course, 5 > 1!")
elseif(5 LESS 1)
message("Oh no, 5 < 1!")
else()
message("Oh my god, 5 == 1!")
endif()
Циклы
Циклы в CMake подобны циклам других языков программирования. В приведённом примере устанавливается значение переменной VARIABLE
в Airport
, а затем четыре вложенные команды последовательно исполняются пока значение переменной VARIABLE
будет равняться Airport
. Последняя четвёртая команда set(VARIABLE "Police station")
устанавливает значение проверяемой переменной в Police station
, поэтому цикл сразу остановится, не дойдя до второй итерации. Команда endwhile()
сигнализирует о завершении списка вложенных в цикл команд.
# Напечатает в консоль три раза "VARIABLE is still 'Airport'":
set(VARIABLE Airport)
while(${VARIABLE} STREQUAL Airport)
message("VARIABLE is still '${VARIABLE}'")
message("VARIABLE is still '${VARIABLE}'")
message("VARIABLE is still '${VARIABLE}'")
set(VARIABLE "Police station")
endwhile()
Данный пример цикла foreach
работает следующим образом: на каждой итерации данного цикла переменной VARIABLE
присваивается следующее значение из списка Give me the sugar please!
, а затем исполняется команда message(${VARIABLE})
, которая выводит текущее значение переменной VARIABLE
. Когда значений в списке не остаётся, то цикл завершает своё выполнение. Команда endforeach()
сигнализирует о завершении списка вложенных в цикл команд.
# Напечатает "Give me the sugar please!" с новых строк:
foreach(VARIABLE Give me the sugar please!)
message(${VARIABLE})
endforeach()
Существуют ещё 3 формы записи цикла foreach
. Первый цикл в данном примере на место списка генерирует целые числа от 0 до 10, второй цикл генерирует в диапазоне от 3 до 15, а третий цикл работает в сегменте от 50 до 90, но с шагом 10.
# Напечатает "0 1 2 3 4 5 6 7 8 9 10" с новых строк:
foreach(VARIABLE RANGE 10)
message(${VARIABLE})
endforeach()
# Напечатает "3 4 5 6 7 8 9 10 11 12 13 14 15" с новых строк:
foreach(VARIABLE RANGE 3 15)
message(${VARIABLE})
endforeach()
# Напечатает "50 60 70 80 90" с новых строк:
foreach(VARIABLE RANGE 50 90 10)
message(${VARIABLE})
endforeach()
Функции и макросы
Синтаксис CMake позволяет определять собственные команды, которые можно будет вызывать в точности как встроенные. Приведённый ниже пример демонстрирует использование функций и макросов: сначала определяются функция и макрос со своими собственными командами, а при их вызове их команды исполняются последовательно.
# Определение функции "print_numbers":
function(print_numbers NUM1 NUM2 NUM3)
message(${NUM1} " " ${NUM2} " " ${NUM3})
endfunction()
# Определение макроса "print_words":
macro(print_words WORD1 WORD2 WORD3)
message(${WORD1} " " ${WORD2} " " ${WORD3})
endmacro()
# Вызов функции "print_numbers", которая напечатает "12 89 225":
print_numbers(12 89 225)
# Вызов макроса "print_words", который напечатает "Hey Hello Goodbye":
print_words(Hey Hello Goodbye)
Команда function
первым аргументов принимает имя будущей функции, а остальные аргументы — это имена параметров, с которыми можно работать как с обычными переменными. Параметры видимы лишь определяемой функции, значит вне функции доступ к её параметрам мы получить не можем. Более того, все другие переменные, определяемые и переопределяемые внутри функции, видны лишь ей самой.
Макросы аналогичны функциям за тем исключением, что они не имеют собственной области видимости: все переменные внутри макросов рассматриваются как глобальные. Более подробно о различиях макросов и функций Вы можете почитать здесь.
Как отметили в комментариях, макросы в CMake подобны макросам в препроцессоре языка Си: если в тело макроса поместить команду return()
, то произойдёт выход из вызвавшей функции (или из всего скрипта), что демонстрирует данный пример:
# Определить макрос, содержащий команду выхода:
macro(demonstrate_macro)
return()
endmacro()
# Определить функцию, вызывающую предыдущий макрос:
function(demonstrate_func)
demonstrate_macro()
message("The function was invoked!")
endfunction()
# Напечатает "Something happened with the function!"
demonstrate_func()
message("Something happened with the function!")
В приведённом выше примере функция demonstrate_func
не успеет напечатать сообщение The function was invoked!
, так как прежде, на место вызова макроса demonstrate_macro
будет подставлена и выполнена команда выхода.
Области видимости
В предыдущем разделе Вы узнали о том, что некоторые конструкции в CMake могут определять собственные области видимости. На самом деле, все переменные по умолчанию считаются глобальными (доступ к ним есть везде), за исключением тех, которые были определены и переопределены в функциях. Также имеются кэш-переменные, у которых своя собственная область видимости, но они применяются не столь часто.
Как упомянули в комментариях, переменные можно определять в «родительской» области видимости с помощью команды set(VARIABLE ... PARENT_SCOPE)
. Данный пример демонстрирует эту особенность:
# Функция, определяющая переменную "VARIABLE" со значением
# "In the parent scope..." в родительской области видимости:
function(demonstrate_variable)
set(VARIABLE "In the parent scope..." PARENT_SCOPE)
endfunction()
# Определить переменную "VARIABLE" в текущей области видимости:
demonstrate_variable()
# Теперь возможно получить к переменной "VARIABLE" доступ:
message("'VARIABLE' is equal to: ${VARIABLE}")
Если из определения переменной VARIABLE
убрать PARENT_SCOPE
, то переменная будет доступна лишь функции demonstrate_variable
, а в глобальной области видимости она примет пустое значение.
Заключение
На этом синтаксис языка CMake заканчивается. Следующая статья выйдет примерно через пару дней и будет вводить в использование системы сборки CMake. До скорых встреч!
Время на прочтение
7 мин
Количество просмотров 458K
CMake — кроcсплатформенная утилита для автоматической сборки программы из исходного кода. При этом сама CMake непосредственно сборкой не занимается, а представляет из себя front-end. В качестве back-end`a могут выступать различные версии make и Ninja. Так же CMake позволяет создавать проекты для CodeBlocks, Eclipse, KDevelop3, MS VC++ и Xcode. Стоит отметить, что большинство проектов создаются не нативных, а всё с теми же back-end`ами.
Для того что бы собрать проект средствами CMake, необходимо в корне дерева исходников разместить файл CMakeLists.txt, хранящий правила и цели сборки, и произвести несколько простых шагов.
Разберёмся на примерах.
Пример 1. Hello, World:
Для начала напишем простейший хеловорлд и создадим структуру проекта:
main.cpp
#include <iostream> int main(int argc, char** argv) { std::cout << "Hello, World!" << std::endl; return 0; }
CMakeLists.txt
cmake_minimum_required(VERSION 2.8) # Проверка версии CMake. # Если версия установленой программы # старее указаной, произайдёт аварийный выход. add_executable(main main.cpp) # Создает исполняемый файл с именем main # из исходника main.cpp
Синтаксис CMake похож на синтаксис bash, всё что после символа «#» является комментарием и обрабатываться программой не будет. CMake позволяет не засорять дерево исходных кодов временными файлами — очень просто и без лишних телодвижений сборка производится «Out-of-Source».
Создадим пустую директорию для временных файлов и перейдём туда.
fshp@panica-desktop:~$ mkdir tmp
fshp@panica-desktop:~$ cd tmp/
fshp@panica-desktop:~/tmp$
Теперь запустим команду cmake, передав ей в качестве параметра путь к папке с исходниками:
fshp@panica-desktop:~/tmp$ cmake ~/cmake/example_1/
…
— Build files have been written to: /home/fshp/tmp
fshp@panica-desktop:~/tmp$
fshp@panica-desktop:~/tmp$ ls
CMakeCache.txt CMakeFiles cmake_install.cmake Makefile
fshp@panica-desktop:~/tmp$
Видим, что в папке появилось несколько временных файлов, необходимых для сборки проекта.
Теперь можно запустить непосредственно make:
fshp@panica-desktop:~/tmp$ make
Scanning dependencies of target main
[100%] Building CXX object CMakeFiles/main.dir/main.cpp.o
Linking CXX executable main
[100%] Built target main
fshp@panica-desktop:~/tmp$ ./main
Hello, World!
fshp@panica-desktop:~/tmp$
Итак, наша программа собралась.
Папку tmp можно очищатьудалять без риска поломать исходники. Если CMakeLists.txt был изменен, то вызов make автоматически запустит cmake. Если исходники были перемещены, то нужно очистить временную директорию и запустить cmake вручную.
Пример 2. Библиотеки:
Если ваш проект содержит библиотеку, то CMake соберет ее без проблем.
Для этого усложним пример.
foo.cpp
#include <iostream> void hello_world() { std::cout << "Hello, World!" << std::endl; }
main.cpp
#include "foo.h" int main(int argc, char** argv) { hello_world(); return 0; }
CMakeLists.txt
cmake_minimum_required(VERSION 2.8) # Проверка версии CMake. # Если версия установленой программы # старее указаной, произайдёт аварийный выход. project(hello_world) # Название проекта set(SOURCE_EXE main.cpp) # Установка переменной со списком исходников для исполняемого файла set(SOURCE_LIB foo.cpp) # Тоже самое, но для библиотеки add_library(foo STATIC ${SOURCE_LIB}) # Создание статической библиотеки с именем foo add_executable(main ${SOURCE_EXE}) # Создает исполняемый файл с именем main target_link_libraries(main foo) # Линковка программы с библиотекой
Переменные могут хранить списки значений, разделённых пробеламитабуляциямипереносами:
set(SOURCE main.cpp foo.cpp)
set(HEADER main.h
foo.h)
Оба варианта правильные
Что бы получить значение переменной ипользуем конструкцию:
${var_name}
Итак, эта версия нашего проекта включает в себя одну статическую библиотеку, собираемую из исходников. Если заменить «STATIC» на «SHARED», то получим библиотеку динамическую. Если тип библиотеки не указать, по умолчанию она соберётся как статическая.
При линковке указываются все необходимые библиотеки:
target_link_libraries(main foo
ogg
vorbis)
Как и при ручной компиляции, имена библиотек указываются без стандартного префикса «lib».
Итак, сборка библиотек с CMake не вызывает проблем, при этом тип библиотеки статическаядинамическая меняется лишь одним параметром.
Пример 3. Подпроекты:
Подпроекты очень удобны, если ваша программа разбита на несколько библиотек или же проект состоит из нескольких программ.
Каждый подпроект является по сути полноценным проектом и может использоваться самостоятельно.
Теперь у нас «foo» находится в субдирректории и там же находится CMakeLists.txt подпроекта.
CMakeLists.txt
cmake_minimum_required(VERSION 2.8) # Проверка версии CMake. # Если версия установленой программы # старее указаной, произайдёт аварийный выход. project(hello_world) # Название проекта set(SOURCE_EXE main.cpp) # Установка переменной со списком исходников include_directories(foo) # Расположение заголовочных файлов add_executable(main ${SOURCE_EXE}) # Создает исполняемый файл с именем main add_subdirectory(foo) # Добавление подпроекта, указывается имя дирректории target_link_libraries(main foo) # Линковка программы с библиотекой
main.cpp
#include "foo.h" int main(int argc, char** argv) { hello_world(); return 0; }
foo/CMakeLists.txt
cmake_minimum_required(VERSION 2.8) # Проверка версии CMake. # Если версия установленой программы # старее указаной, произайдёт аварийный выход. project(foo) # Название проекта set(SOURCE_LIB foo.cpp) # Установка переменной со списком исходников add_library(foo STATIC ${SOURCE_LIB})# Создание статической библиотеки
foo/foo.cpp
#include <iostream> void hello_world() { std::cout << "Hello, World!" << std::endl; }
В файле подпроекта ничего нового для вас нет. А вот в основном файле новые команды:
include_directories(foo)
main.cpp мы не меняли, а foo.h перенесли. Команда указывает компилятору, где искать заголовочные файлы. Может быть вызвана несколько раз. Хидеры будут искаться во всех указаных директориях.
add_subdirectory(foo)
Указываем директорию с подпроектом, который будет собран как самостоятельный.
Вывод: проекты на CMake можно объединять в довольно сложные иерархические структуры, причем каждый подпроект в реальности является самостоятельным проектом, который в свою очередь может сам состоять из подпроектов. Это позволяет легко разбить вашу программу на необходимое количество отдельных модулей. Примером такого подхода может служить KDE.
Пример 4. Поиск библиотек:
CMake обладает достаточно развитыми средствами поиска установленых библиотек, правда они не встроеные, а реализованы в виде отдельных модулей. В стандартной поставке довольно много модулей, но некоторые проекты (например Ogre) поставляют свои. Они позволяют системе автоматически определить наличие необходимых для линковки проекта библиотек.
На debian модули располагаются в /usr/share/cmake-2.8/Modules/ (у вас версия может отличаться). За поиск библиотек отвечают модули, называющиеся FindNAME.cmake, где NAME — имя библиотеки.
find_package(SDL REQUIRED)
if(NOT SDL_FOUND)
message(SEND_ERROR "Failed to find SDL")
return()
else()
include_directories(${SDL_INCLUDE_DIR})
endif()
##########################################################
find_package(LibXml2 REQUIRED)
if(NOT LIBXML2_FOUND)
message(SEND_ERROR "Failed to find LibXml2")
return()
else()
include_directories(${LIBXML2_INCLUDE_DIR})
endif()
##########################################################
find_package(Boost COMPONENTS thread-mt REQUIRED)
if(NOT Boost_FOUND)
message(SEND_ERROR "Failed to find boost::thread-mt.")
return()
else()
include_directories(${Boost_INCLUDE_DIRS})
endif()
##########################################################
target_link_libraries(${TARGET}
${SDL_LIBRARY}
${LIBXML2_LIBRARIES}
${Boost_LIBRARIES})
Думаю, смысл должен быть понятен. Первый и второй блок — поиск библиотеки. Если в системе её нет, выведется сообщение об ошибке и завершается выполнение cmake. Третий блок похож, только он ищет не целый пакет библиотек, а лишь необходимый компонент. Каждый такой автоматизированый поиск определяет после выполнения как минимум 3 переменные:
SDL_FOUND, LIBXML2_FOUND, Boost_FOUND — признак присутствия бибилиотеки;
SDL_LIBRARY, LIBXML2_LIBRARIES, Boost_LIBRARIES — имена библиотек для линковки;
SDL_INCLUDE_DIR, LIBXML2_INCLUDE_DIR, Boost_INCLUDE_DIRS — пути к заголовочным файлам.
Если с первыми более или менее понятно, то вторые и третьи мне доставили много хлопот — половина имеет имена в единственном числе, половина — во множественном. Но оказалось, это легко отследить. В каждом модуле вначале есть коментарии, там описаны определяемые переменные. Посмотрите, например, /usr/share/cmake-2.8/Modules/FindLibXml2.cmake
Как видите, CMake способен сам определить наличие и местоположение необходимых библиотек и заголовочных файлов. В принципе, это должна уметь любая система автоматической сборки, иначе смысл в ней?
Пример 5. Внешние библиотеки и объектные файлы:
Если вы пишите для «дяди», а злой «дядя» любит самописные библиотеки и делиться исходниками не желает, поэтому присылает готовую библиотеку, то вы по адресу.
Объектные файлы в CMake стоят на ряду с исходниками — достаточно включить объектник в список файлов для компиляции.
С библиотеками потуже. Как известно, статическая библиотека это не что иное, как ar-архив, внутри которого лежат обычные объектники, никак не связаные между собой. Вы, наверное, уже догадались, как я поступал сначала. Да, просто потрошил библиотеку. Но потом был найден способ поэлегантнее:
add_library(netutil STATIC IMPORTED)
set_property(TARGET netutil PROPERTY
IMPORTED_LOCATION Binary/game_client/libnetutil.a)
Слово «IMPORTED», указывает, что библиотека берётся извне.
В CMake каждая цель имеет параметры, а set_property позволяет их изменять.
Линкуется такая библиотека стандартно:
target_link_libraries(${TARGET} netutil)
Для динамических библиотек все аналогично, только тип «SHARED», расширение — «.so».
К сожалению, поддержка несистемных библиотек реализована немного костыльно. Возможно, я просто не знаю правильного варианта, поэтому буду рад, если «ткнете мордочкой». С другой стороны это не навороченый экзоскелет с системой жизнеобеспечения, а простейший костыль из двух строк.
Генераторы:
Как было сказано в начале, CMake умеет генерировать множество различных видов проектов. Это удобно и позволяет использовать CMake для практически любой популярной IDE.
Если запустить cmake без параметров, в конце будут описаны доступные генераторы. Пользоваться так:
fshp@panica-desktop:~/tmp$ cmake ~/cmake/example_3/ -G «KDevelop3 — Unix Makefiles»
Заключение:
Это не перевод мануала, а результат использования CMake в одном коммерческом проекте. Буду рад, если статья поможет хотя бы одному человеку — на русском языке подобной документации довольно мало.
Чем понравился CMake лично мне:
- один проект — один файл. Не нужно хранить кучу скриптов настройки, сборки и прочего хлама;
- Скорость работы в сравнении с autotools;
- простой и понятный синтаксис, конечно с элегантностью питона не потягаться, но и не брейнфак, в конце концов.;
- является front-end`ом для множества IDE;
- отображение прогресса — довольно удобно;
- цветной вывод — в серые будни немного краски не помешает;
Для Sublime Text есть плагин, добавляющий подсветку синтаксиса CMake, он так и называется — «CMake».
Примеры
CMake — это открытый и кросс-платформенный набор утилит, предназначенных для автоматизации тестирования, компиляции и создания пакетов проектов на C/C++. Написав однажды небольшой и понятный всем скрипт, Вы тем самым обеспечите одинаковую сборку вашего проекта на любых платформах, где доступен CMake.
Язык CMake, будучи транслированным в нативный файл сборки (например, Makefile или Ninja), определяет процесс всего управления проектом. В Вашем распоряжении, с функциональной стороны, есть лишь команды, которые могут образовываться в довольно сложные конструкции. С них мы и начнём.
Ниже приведены примеры использования языка CMake, по которым Вам следует попрактиковаться. Экспериментируйте с исходным кодом, меняя существующие команды и добавляя новые. Чтобы запустить данные примеры, следуйте этим шагам:
- Установите программу CMake с официального сайта
- Создайте на рабочем столе текстовый файл
CMakeLists.txt
- Добавьте в начало файла
cmake_minimum_required(VERSION 3.0)
- Скопируйте туда исходные тексты необходимых примеров
- Если у Вас установлен консольный CMake, то запустить скрипт можно с помощью команды «
cmake .
«. Если у Вас графический CMake, то в первые два верхних поля приложения вбейте адрес Вашего рабочего стола, затем нажмите кнопкуGenerate
. Результат появится в нижнем текстовом поле.
Команды в CMake подобны функциям во многих языках программирования. Чтобы вызвать команду, необходимо написать её имя, а затем передать ей обрамлённые в круглые скобки аргументы, отделённые символами пробелов. В приведённом примере команде message
передаются шесть аргументов для вывода в консоль:
# Напечатает в консоль "CMake is the most powerful buildsystem!"
message("CMake " "is " "the " "most " "powerful " "buildsystem!")
Аргументы, обрамлённые в двоёные кавычки, позволяют внутри себя совершать экранирование и подстановку переменных. Необрамлённые аргументы не позволяют производить подобных вещей и не могут включать в себя символов ()#"
и пробелов, однако более удобны для использования. Пример:
# Напечатает "Hello, my lovely CMake", один таб и "!":
message("Hello, my lovely CMaket!")
# Напечатает "Hello,_my_lovely_CMake!" без пробелов:
message(Hello,_my_lovely_CMake!)
Комментарии начинаются с символа решётки и заканчиваются на конце той строки, где они были напечатаны. Текст, заключённый в комментариях, игнорируется системой сборки и не оказывает никакого эффекта на её работе. Примеры выше также демонстрируют использование комментариев.
Переменные можно определить путём вызова команды set
, а удалить вызовом unset
. Получить значение переменной можно по конструкции ${VARIABLE}
. Пример:
# Определить переменную VARIABLE со значением "Mr. Thomas":
set(VARIABLE "Mr. Thomas")
# Напечает "His name is: Mr. Thomas":
message("His name is: " ${VARIABLE})
# Удалить переменную VARIABLE:
unset(VARIABLE)
Прежде чем приступать к изучению условных операторов и циклических конструкций, необходимо понимать работу логических выражений. Логические выражения используются при проверки условий и могут принимать одно из двух значений: правда или ложь. Например, выражение 52 LESS 58
обратится в правду, так как 52 < 58. Выражение 88 EQUAL 88
обратится в правду, 63 GREATER 104
обратится в ложь. Сравнивать можно не только числа, но и строки, версии, файлы, принадлежность к списку и регулярные выражения. Полный список логических выражений можно посмотреть тут.
Условные операторы в CMake работают в точности как в других языках программирования. В данном примере сработает лишь первый условный оператор, который проверяет, что 5 > 1. Второе и третье условия ложны, так как 5 не может быть меньше или равняться одному. Блоки команд elseif(5 LESS 1)
и else()
необязательны, а endif()
обязательна и сигнализирует о завершении предыдущих проверок.
# Напечатает "Of course, 5 > 1!":
if(5 GREATER 1)
message("Of course, 5 > 1!")
elseif(5 LESS 1)
message("Oh no, 5 < 1!")
else()
message("Oh my god, 5 == 1!")
endif()
Циклы в CMake подобны циклам других языков программирования. В приведённом примере устанавливается значение переменной VARIABLE
в Airport
, а затем четыре вложенные команды последовательно исполняются пока значение переменной VARIABLE
будет равняться Airport
. Последняя четвёртая команда set(VARIABLE "Police station")
устанавливает значение проверяемой переменной в Police station
, поэтому цикл сразу остановится, не дойдя до второй итерации. Команда endwhile()
сигнализирует о завершении списка вложенных в цикл команд.
# Напечатает в консоль три раза "VARIABLE is still 'Airport'":
set(VARIABLE Airport)
while(${VARIABLE} STREQUAL Airport)
message("VARIABLE is still '${VARIABLE}'")
message("VARIABLE is still '${VARIABLE}'")
message("VARIABLE is still '${VARIABLE}'")
set(VARIABLE "Police station")
endwhile()
Данный пример цикла foreach
работает следующим образом: на каждой итерации данного цикла переменной VARIABLE
присваивается следующее значение из списка Give me the sugar please!
, а затем исполняется команда message(${VARIABLE})
, которая выводит текущее значение переменной VARIABLE
. Когда значений в списке не остаётся, то цикл завершает своё выполнение. Команда endforeach()
сигнализирует о завершении списка вложенных в цикл команд.
# Напечатает "Give me the sugar please!" с новых строк:
foreach(VARIABLE Give me the sugar please!)
message(${VARIABLE})
endforeach()
Существуют ещё 3 формы записи цикла foreach
. Первый цикл в данном примере на место списка генерирует целые числа от 0 до 10, второй цикл генерирует в диапозоне от 3 до 15, а третий цикл работает в сегменте от 50 до 90, но с шагом 10.
# Напечатает "0 1 2 3 4 5 6 7 8 9 10" с новых строк:
foreach(VARIABLE RANGE 10)
message(${VARIABLE})
endforeach()
# Напечатает "3 4 5 6 7 8 9 10 11 12 13 14 15" с новых строк:
foreach(VARIABLE RANGE 3 15)
message(${VARIABLE})
endforeach()
# Напечатает "50 60 70 80 90" с новых строк:
foreach(VARIABLE RANGE 50 90 10)
message(${VARIABLE})
endforeach()
Синтаксис CMake позволяет определять собственные команды, которые можно будет вызывать в точности как встроенные. Приведённый ниже пример демонстрирует использование функций и макросов: сначала определяются функция и макрос со своими собственными командами, а при их вызове их команды исполняются последовательно.
# Определение функции "print_numbers":
function(print_numbers NUM1 NUM2 NUM3)
message(${NUM1} " " ${NUM2} " " ${NUM3})
endfunction()
# Определение макроса "print_words":
macro(print_words WORD1 WORD2 WORD3)
message(${WORD1} " " ${WORD2} " " ${WORD3})
endmacro()
# Вызов функции "print_numbers", которая напечатает "12 89 225":
print_numbers(12 89 225)
# Вызов макроса "print_words", который напечатает "Hey Hello Goodbye":
print_words(Hey Hello Goodbye)
Команда function
первым аргументов принимает имя будущей функции, а остальные аргументы — это имена параметров, с которыми можно работать как с обычными переменными. Параметры видимы лишь определяемой функции, значит вне функции доступ к её параметрам мы получить не можем. Более того, все другие переменные, определяемые и переопределяемые внутри функции, видны лишь ей самой.
Макросы аналогичны функциям за тем исключением, что они не имеют собственной области видимости: все переменные внутри макросов рассматриваются как глобальные. Более подробно о различиях макросов и функций Вы можете почитать здесь.
В предыдущем разделе Вы узнали о том, что некоторые конструкции в CMake могут определять собственные области видимости. На самом деле, все переменные по умолчанию считаются глобальными (доступ к ним есть везде), за исключением тех, которые были определены и переопределены в функциях. Также имеются кэш-переменные, у которых своя собственная область видимости, но они применяются не столь часто.
На этом синтаксис языка CMake заканчивается. Следующая статья выйдет примерно через пару дней и будет вводить в использование системы сборки CMake. До скорых встреч!
Let’s block ads! (Why?)
Добавлено 7 января 2023 в 23:23
С чего начать работу с CMake? Этот шаг познакомит вас с базовыми синтаксисом, некоторыми командами и переменными CMake. По мере введения этих концепций мы проработаем три упражнения и создадим простой проект CMake.
Каждое упражнение на этом этапе начинается с некоторой исходной информации. Затем предоставляется цель и список полезных ресурсов. Каждый файл в разделе «Редактируемые файлы» находится в каталоге Step1 и содержит один или несколько комментариев TODO. Каждое TODO представляет собой одну или две строки кода, которые нужно изменить или добавить. TODO предназначены для выполнения в порядке нумерации, сначала выполните TODO 1, затем TODO 2 и т. д. В разделе «С чего начать» вы найдете несколько полезных советов и руководство по выполнению упражнения. Затем в разделе «Сборка и запуск» будет пошагово показано, как создать и протестировать упражнение. Наконец, в конце каждого упражнения обсуждается предполагаемое решение.
Также обратите внимание, что каждый шаг в руководстве основан на следующем. Так, например, исходный код для Шага 2 является полным решением для Шага 1.
Упражнение 1. Создание базового проекта
Самый простой проект CMake – это исполняемый файл, созданный из одного файла исходного кода. Для таких простых проектов достаточно файла CMakeLists.txt с тремя командами.
Примечание. Хотя CMake поддерживает команды, набранные в верхнем, нижнем и смешанном регистре, использование нижнего регистра предпочтительнее и будет использоваться на протяжении всего руководства.
Самый популярный файл CMakeLists.txt любого проекта должен начинаться с указания минимальной версии CMake с помощью команды cmake_minimum_required()
. Она устанавливает параметры политики и гарантирует, что следующие функции CMake выполняются с совместимой версией CMake.
Чтобы начать проект, мы используем команду project()
для установки имени проекта. Этот вызов требуется для каждого проекта и должен вызываться вскоре после cmake_minimum_required()
. Как мы увидим позже, эту команду также можно использовать для указания другой информации на уровне проекта, такой как язык или номер версии.
Наконец, команда add_executable()
указывает CMake создать исполняемый файл, используя указанные файлы исходного кода.
Цель
Узнать, как создать простой проект CMake.
Полезные ресурсы
add_executable()
cmake_minimum_required()
project()
Редактируемые файлы
- CMakeLists.txt
С чего начать
Исходный код tutorial.cxx находится в каталоге Help/guide/tutorial/Step1 и может использоваться для вычисления квадратного корня числа. Этот файл на данном шаге редактировать не нужно.
В том же каталоге находится файл CMakeLists.txt, который вы должны заполнить. Начните с TODO 1 и проработайте до TODO 3.
Сборка и запуск
Как только TODO 1 – TODO 3 будут выполнены, мы будем готовы собрать и запустить наш проект! Сначала запустите исполняемый файл cmake или cmake-gui, чтобы сконфигурировать проект, а затем соберите его с помощью выбранного инструмента сборки.
Например, из командной строки мы можем перейти в каталог Help/guide/tutorial дерева исходного кода CMake и создать каталог сборки:
mkdir Step1_build
Затем перейдите в этот каталог сборки и запустите cmake, чтобы настроить проект и создать файлы нативной системы сборки:
cd Step1_build
cmake ../Step1
Затем вызовите эту систему сборки, чтобы скомпилировать/слинковать проект:
cmake --build .
Наконец, попробуйте использовать только что собранный Tutorial с этими командами:
Tutorial 4294967296
Tutorial 10
Tutorial
Решение
Как упоминалось выше, всё, что нам нужно для запуска, – это трехстрочный файл CMakeLists.txt. Первая строка – используем cmake_minimum_required()
для установки версии CMake следующим образом:
TODO 1
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
Следующим шагом для создания базового проекта является использование команды project()
следующим образом, чтобы установить имя проекта:
TODO 2
CMakeLists.txt
project(Tutorial)
Последней командой для вызова базового проекта является add_executable()
. Мы вызываем его так:
TODO 3
CMakeLists.txt
add_executable(Tutorial tutorial.cxx)
Упражнение 2. Определение стандарта C++
В CMake есть специальные переменные, которые либо создаются за кулисами, либо имеют значение для CMake, если они установлены кодом проекта. Многие из этих переменных начинаются с CMAKE_
. Согласно соглашению об именовании избегайте этого префикса при создании переменных для ваших проектов. Две из этих специальных устанавливаемых пользователем переменных – CMAKE_CXX_STANDARD
и CMAKE_CXX_STANDARD_REQUIRED
. Их можно использовать вместе, чтобы указать стандарт C++, необходимый для сборки проекта.
Цель
Добавить функцию, которая требует C++11.
Полезные ресурсы
CMAKE_CXX_STANDARD
CMAKE_CXX_STANDARD_REQUIRED
set()
Редактируемы файлы
- CMakeLists.txt
- tutorial.cxx
С чего начать
Продолжаем редактировать файлы в каталоге Step1. Начните с TODO 4 и завершите TODO 6.
Во-первых, отредактируйте tutorial.cxx, добавив функцию, для которой требуется C++11. Затем обновите CMakeLists.txt, чтобы он требовал C++11.
Сборка и запуск
Давайте снова соберем наш проект. Поскольку мы уже создали каталог сборки и запустили CMake для упражнения 1, мы можем перейти к этапу сборки:
cd Step1_build
cmake --build .
Теперь мы можем попробовать использовать только что собранное приложение Tutorial с теми же командами, что и раньше:
Tutorial 4294967296
Tutorial 10
Tutorial
Решение
Начнем с добавления в наш проект функций C++11, заменив atof
на std::stod
в tutorial.cxx. Это выглядит следующим образом:
TODO 4
tutorial.cxx
const double inputValue = std::stod(argv[1]);
Чтобы выполнить TODO 5, просто удалите #include <cstdlib>
.
Нам нужно будет явно указать в коде CMake, что он должен использовать правильные флаги. Один из способов включить поддержку определенного стандарта C++ в CMake – использовать переменную CMAKE_CXX_STANDARD
. Для этого руководства установите для переменной CMAKE_CXX_STANDARD
в файле CMakeLists.txt значение 11, а для CMAKE_CXX_STANDARD_REQUIRED
– значение True
. Объявления CMAKE_CXX_STANDARD
обязательно добавьте перед вызовом add_executable()
.
TODO 6
CMakeLists.txt
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
Упражнение 3. Добавление номера версии и сконфигурированного заголовочного файла
Иногда бывает полезно, чтобы переменная, определенная в файле CMakelists.txt
, также была доступна в исходном коде. В этом случае мы хотели бы распечатать версию проекта.
Один из способов добиться этого – использовать сконфигурированный заголовочный файл. Мы создаем входной файл с одной или несколькими переменными для замены. Эти переменные имеют специальный синтаксис, похожий на @VAR@
. Затем мы используем команду configure_file()
, чтобы скопировать входной файл в заданный выходной файл и заменить эти переменные текущим значением VAR
в файле CMakelists.txt.
Хотя мы могли бы редактировать версию непосредственно в исходном коде, использование этой функции предпочтительнее, поскольку она создает единый источник и позволяет избежать дублирования.
Цель
Определить и сообщить номер версии проекта.
Полезные ресурсы
<PROJECT-NAME>_VERSION_MAJOR
<PROJECT-NAME>_VERSION_MINOR
configure_file()
target_include_directories()
Редактируемые файлы
- CMakeLists.txt
- tutorial.cxx
С чего начать
Продолжаем редактировать файлы из Step1. Начните с TODO 7 и завершите TODO 12. В этом упражнении мы начнем с добавления номера версии проекта в CMakeLists.txt. В том же файле используйте configure_file()
, чтобы скопировать заданный входной файл в выходной файл и заменить некоторые значения переменных в содержимом входного файла.
Затем создайте входной заголовочный файл TutorialConfig.h.in, определяющий номера версий, которые будут принимать переменные, переданные из configure_file()
.
Наконец, обновите tutorial.cxx, чтобы распечатать номер его версии.
Сборка и запуск
Давайте снова соберем наш проект. Как и раньше, мы уже создали каталог сборки и запустили CMake, поэтому можем перейти к этапу сборки:
cd Step1_build
cmake --build .
Убедитесь, что теперь при запуске исполняемого файла без каких-либо аргументов выводится номер версии.
Решение
В этом упражнении мы улучшаем наш исполняемый файл с помощью вывода номера версии. Хотя мы могли бы сделать это исключительно в исходном коде, использование CMakeLists.txt позволяет нам поддерживать единый источник данных для номера версии.
Сначала мы модифицируем файл CMakeLists.txt, чтобы использовать команду project()
для установки имени проекта и номера версии. Когда вызывается команда project()
, CMake за кулисами определяет Tutorial_VERSION_MAJOR
и Tutorial_VERSION_MINOR
.
TODO 7
CMakeLists.txt
project(Tutorial VERSION 1.0)
Затем мы используем configure_file()
для копирования входного файла с заменой переменных, указанных CMake:
TODO 8
CMakeLists.txt
configure_file(TutorialConfig.h.in TutorialConfig.h)
Поскольку сконфигурированный файл будет записан в бинарный каталог проекта, мы должны добавить этот каталог в список путей для поиска включаемых файлов.
Примечание. В этом руководстве мы будем ссылаться на сборку проекта и каталог бинарных файлов проекта как синонимы. Они одинаковы и не означают ссылку на каталог bin/.
Используем target_include_directories()
, чтобы указать, где исполняемая цель должна искать включаемые файлы.
TODO 9
CMakeLists.txt
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
)
TutorialConfig.h.in – это входной заголовочный файл, который необходимо сконфигурировать. Когда configure_file()
вызывается из нашего CMakeLists.txt, значения для @Tutorial_VERSION_MAJOR@
и @Tutorial_VERSION_MINOR@
будут заменены в TutorialConfig.h соответствующими номерами версий из проекта.
TODO 10
TutorialConfig.h.in
// сконфигурированные параметры и настройки для Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
Затем нам нужно изменить tutorial.cxx, включив в него сконфигурированный заголовочный файл TutorialConfig.h.
TODO 11
tutorial.cxx
#include "TutorialConfig.h"
Наконец, мы распечатываем имя исполняемого файла и номер версии, обновляя tutorial.cxx следующим образом:
TODO 12
tutorial.cxx
if (argc < 2) {
// сообщить версию
std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
<< Tutorial_VERSION_MINOR << std::endl;
std::cout << "Usage: " << argv[0] << " number" << std::endl;
return 1;
}
Теги
C++ / CppCMakeАвтоматизация сборкиПрограммированиеСистема сборки
Эта статья находится в разработке!
Содержание
- 1 Что это и зачем нужно
- 1.1 Краткое описание
- 2 Старт
- 3 Подробное описание
- 3.1 Указание необходимой версии cmake
- 3.2 Название проекта
- 3.3 Переменные
- 3.4 Устанавливаем команды компилятору
- 3.5 Папка с хедерами
- 3.6 Самое важное — подключение библиотек
- 3.7 Пример хорошего CMakeLists.txt и где он будет лежать
- 3.8 Как создать библиотеку в поддиректории и слинковать ее с основной программой
- 4 Как использовать CMake в связке с QtCreator
- 4.1 Как добавить header в проект, чтобы его было видно в списке файлов
Что это и зачем нужно
CMake — кроссплатформенная автоматизированная система сборки проектов.
Непосредственно сборкой она не занимается, а только генерирует Makefile, который потом будет выполнен утилитой make.
CMake может проверять наличие необходимых библиотек и подключать их, собирать проекты под разными компиляторами и операционными системами. Т.е. у вас есть куча кода и файлик, содержащий информацию для cmake, и чтобы скомпилить
это дело где-нибудь еще, вам нужно просто запустить там cmake, который сделает всё сам. Удобно, полезно, просто.
Краткое описание
Если нет желания/времени/сил читать весь туториал и Вы используете какой-нибудь QtCreator (или любая другая IDE, умеющая работать с cmake), то:
- Создайте в IDE проект под cmake
- Найдите в папке с проектом CMakeFiles.txt
- Пробегитесь глазами по туториалу, соотнося его с вашим CMakeFiles.txt
Про подключение библиотек рекомендуется все-таки прочитать целиком.
Старт
Предполагается, что найти и скачать сам cmake ты, %username%, в состоянии. //а если нет?
Предположим, у Вас есть исходничек «test.cpp» (// а если нет?)(А если нет, то CMake тебе трогать рано).
Для начала нужно создать файлик для cmake, который обычно называют «CMakeLists.txt», и написать туда вот это:
add_executable(test test.cpp)
Теперь запускаем (из консоли) в этой папке команду «cmake CMakeLists.txt» (аргументом можно передавать не только файл, но и директорию, в которой он лежит, тогда cmake найдет его сам).
cmake будет использовать переданный (или найденный) файл проекта (тот самый CMakeLists.txt), и в текущей директории будет создавать проект.
Проект — это много-много файлов и директорий (примечание: поэтому лучше запускать cmake из другой директории, чтобы можно было, например, быстро удалить все бинарники), из которых нас больше всего интересует Makefile.
Makefile — это файл, нужный для утилиты make.
Именно она запускает компиляторы, линковщики и прочие радости. Запускаем make в каталоге сборки (т.е. там же, где Вы запускали cmake).
В консоли вылезет примерно такой текст:
Scanning dependencies of target test [100%] Building CXX object CMakeFiles/test.dir/test.cpp.o Linking CXX executable test [100%] Built target test
А у Вас в папочке появится исполняемый файл «test». Запустите, убедитесь, что это действительно то, что ожидается от компиляции файла «test.cpp».
Подробное описание
Поразбираемся с различными возможностями cmake.
Указание необходимой версии cmake
cmake_minimum_required(VERSION 2.6)
Указывайте высокую минимальную версию CMake.
Если используемая версия cmake меньше 2.6, он не захочет работать. Писать эту команду всегда — хороший стиль (cmake будет пыхтеть и обижаться, если вы не укажете версию, но собирать всё равно всё будет).
Название проекта
project(visualization)
Указывает, что этот cmake-файл является корневым для некоторого проекта. С проектами связаны определенные переменные и поведение cmake (читайте документацию).
Переменные
В cmake можно создавать текстовые переменные. Команда
set(VARIABLE The variable's value)
запишет в переменную «VARIABLE» значение «The variable’s value».
Чтобы где-либо использовать значение этой переменной, нужно написать ${VARIABLE}.
Чтобы добавить к переменной некий текст, можно сделать так:
set(VARIABLE "${VARIABLE} new text")
Как видите, использовать значение можно и внутри кавычек.
Переменные активно используются различными библиотеками — для установки флагов, параметров сборки/линковки и прочих вкусностей, об этом чуть-чуть попозже.
Пример коше’гного проекта со списком сорцов в отдельной переменной:
cmake_minimum_required(VERSION 2.6) set(SOURCES test.cpp lib1.cpp lib2.cpp) add_executable(test ${SOURCES})
Устанавливаем команды компилятору
add_definitions(-DSOME_IMPORTANT_DEFINITION)
Эта команда используется для установки дефайнов, которыe можно проверить в коде через, например, #ifdef SOME_IMPORTANT_DEFINITION.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
Эта команда добавит к флагам, используемым при сборке c++-кода, флаги -std=c++11 и -Wall.
Кто не знает: «-std=c++11» включает в gcc поддержку стандарта c++11, «-Wall» говорит gcc выводить все предупреждения (очень советую, помогает отловить много глупых багов и писать аккуратный код).
Если ваша версия GCC меньше, чем 4.7.0, вместо -std=c++11 нужно использовать -std=c++0x.
В GCC 4.8.0 появился флаг -std=c++1y, в котором начинают реализовывать фичи следующего стандарта.
Папка с хедерами
Допустим, Вы хотите, чтобы хедеры (файлики, подключаемые через #include) искались еще и в каталогах «headers/» и «more_headers/»:
include_directories("headers/" "more_headers/")
Надеюсь, и это понятно.
Самое важное — подключение библиотек
Научимся искать и подключать библиотеки при помощи cmake на примере Boost.
Для начала установим переменные для буста:
set(Boost_USE_STATIC_LIBS OFF) set(Boost_USE_MULTITHREADED ON)
Первое — мы не хотим, чтобы буст подключался к нам статически (т.е. хотим динамическую линковку). Если ты, %username%, не знаешь, что это, пока просто забей и используй этот флаг так, как написано. Но в ближайшее время узнай, о чем речь.
Второй флаг разрешает бусту внутри своих магических реализаций использовать треды для распараллеливания и прочих радостей.
Итак, мы установили флаги. Давайте найдем буст!
Допустим, нам нужны компоненты буста под названием chrono (библиотека для работы со временем) и filesystem (библиотека для работы с файловой системой):
find_package(Boost COMPONENTS chrono filesystem REQUIRED)
Win, будут искаться только нужные библиотеки, и их расположение будет записано в переменную Boost_LIBRARIES.
Опция «REQUIRED» говорит о том, что библиотека необходима проекту.
Без нее cmake решит, что отсутствие данной библиотеки — не так уж и страшно, и будет собирать дальше.
Добавим директории с хедерами буста для поиска в них хедеров:
include_directories(${Boost_INCLUDE_DIRS})
Итак, осталось найденные библиотеки подключить к исполняемому файлу.
target_link_libraries(test ${Boost_LIBRARIES})
В качестве библиотек нужно указать пути к необходимым собранным библиотекам. cmake нашел указанные нами библиотеки и записал в переменную, чем мы и пользуемся.
Заметим, что эту команду нужно вызывать после того, как создан target сборки (через add_executable).
Пример хорошего CMakeLists.txt и где он будет лежать
Итак, полный пример использования всего этого. У нас есть некая директория (отныне считаем ее «/sources»), и в ней лежат исходники
/sources/lib1/main.cpp /sources/lib2/main.cpp /sources/main.cpp
В корне «/» лежит файл «/CMakeLists.txt»:
cmake_minimum_required(VERSION 2.8) project(cmake-example) set(Boost_USE_STATIC_LIBS OFF) set(Boost_USE_MULTITHREADED ON) find_package(Boost COMPONENTS chrono filesystem REQUIRED) set(CMAKE_CXX_FLAGS "$$${CMAKE_CXX_FLAGS} -std=c++11 -Wall") set(SRC_LIST lib1/main.cpp lib2/main.cpp main.cpp) add_executable($$${PROJECT_NAME} $$${SRC_LIST}) target_link_libraries($$${PROJECT_NAME} $$${Boost_LIBRARIES})
Если Вам что-то в нём не понятно — перечитайте соответствующую информацию выше.
Создаем директорию «/build» (не «/sources/build»), переходим в нее, запускаем в ней «cmake ..».
«..» — метка родительской директории.
cmake возьмет из нее наш CMakeLists.txt и по нему создаст проект в папке «/build».
Чтобы проект собрать, запускаем «make» в той же папке «/build».
Таким образом, в корне у нас есть:
- CMakeLists.txt
- директория с исходниками
- каталог сборки
Все разделено, автоматизировано и удобно.
Как создать библиотеку в поддиректории и слинковать ее с основной программой
Пусть в ./ лежит основной проект, а в ./subdir мы хотим сделать либу, а в ./build построить проект.
./subdir/CMakeLists.txt
project(MegaLibrary) set(SOURCES lib.cpp) set(HEADERS lib.h) add_library(lib $$${SOURCES} $$${HEADERS}) target_include_directories(lib PUBLIC $$${CMAKE_CURRENT_SOURCE_DIR})
./CMakeLists.txt
project(MainProject) set(MAIN_PROJECT_SRC_LIST main) # Other stuff
add_executable(main $$${MAIN_PROJECT_SRC_LIST}) add_subdirectory(subdir) target_link_libraries(main lib)
Теперь можно в файлах основного проекта делать #include «lib.h» (см. документацию по target_include_directories).
В ./build запускаем «cmake .. && make» и получаем собранный проект.
Как использовать CMake в связке с QtCreator
Интеграция с cmake у QtCreator не очень тесная, тем не менее, работать с ним можно.
Создаем новый проект без использования Qt, выбираем «Проект на С++ с использованием CMake». Создастся дефолтный файл сборки, который просто добавляет все исходники в директории проекта и компилирует их в один бинарник.
Если вы создали файл header.h в директорию проекта, просто строчку
add_executable($$${PROJECT_NAME} $$${SRC_LIST})
измените на
add_executable($$${PROJECT_NAME} $$${SRC_LIST} "header.h")