- Знакомство с CMake. Часть 1. Установка, CMakeLists.txt, сборка.
- Введение.
- Установка в Linux.
- Установка в Windows.
- Cmake windows что это
- Структура CMakeLists.txt
- Автогенерация проекта для Visual Studio (Windows)
- Зависимости между библиотеками и приложениями
- Выбор стандарта и диалекта C++
- Функции в CMake
- Добавление исходников к цели с target_sources
- Интерфейс к утилитам командной строки
- Функция find_package
- Для чего нужен CMake?
- 5 ответов 5
- Что есть что в CMake 3.10+ и как это использовать
- Вступление
- Обзор демонстрационного проекта
- Используемые сущности и понятия
- Примеры задач и использование доступных инструментов
- Заключение
- Полезные ссылки
Знакомство с CMake. Часть 1. Установка, CMakeLists.txt, сборка.
Введение.
Многие, кто начинал создавать собственные программы, пользовался какой-либо системой сборки. В общем, система сборки – это набор инструментов, облегчающий работу с компилятором. Это включает в себя компиляцию, линковку, установку, а также сбор исходных файлов для передачи их компилятору и слежение за зависимостями. Также современные системы сборки облегчают работу с библиотеками, позволяют создавать переносимые проекты и выполняют ещё массу других вкусностей. Эта статья посвящена популярной системе сборки CMake и расскажет, как правильно её установить и настроить, а также будет рассмотрен простой пример её использования. Она рассчитана на тех, что хоть немного знаком с понятиями make, Makefile, компиляция, линковка.
Установка в Linux.
Для популярных дистрибутивов Linux типа Debian, Gentoo, Fedora и т.д. CMake давно лежит в официальных репозиториях. Нам нужно всего лишь установить пакет cmake с помощью менеджера пакетов. Как правило, он устанавливается в системные директории, и необходимости править переменные окружения нету. Можете проверить её работоспособность, выполнив
Если же в репозитории нет такого пакета, то можно его собрать вручную. Скачиваем Unix/Linux Source, например, cmake-3.5.0-rc3.tar.gz, распаковываем и собираем:
Если нет необходимости устанавливать в системную /usr директорию, можно в аргументе —prefix прописать нужный корень установки. По умолчанию, без явного указания —prefix, установка будет произведена в /usr/local. -j используется для ускорения сборки, например, на 4-х ядерном процессоре можно указать -j4, и сборка будет вестись параллельно в 4 потока.
Установка в Windows.
Для Windows на сайте CMake лежит установочный файл msi. Рекомендую при установке отметить галочку добавления пути в переменные окружения PATH для всех пользователей. Тогда, после перелогинивания, CMake будет доступен из любого места. Проверить можно, открыв cmd и выполнив тот же
Cmake windows что это
На первом шаге проект нужно сконфигурировать, то есть создать финальный скрипт сборки, запустив cmake в будущем каталоге сборки.
На втором шаге нужно запустить финальный скрипт. Не вызывайте make ! Утилита cmake сделает это сама:
Структура CMakeLists.txt
В начале главного файла CMakeLists.txt ставят метаинформацию о минимальной версии CMake и названии проекта:
Затем следует список инструкций, служащих для вычисления различных переменных, создания целей сборки, подключения проектов из подкаталогов и так далее. Например, подключить дополнительный CMakeLists.txt из подкаталога можно так:
Целью может стать исполняемый файл, собираемый из исходного кода
Целью также может быть библиотека, статическая или динамическая.
Автогенерация проекта для Visual Studio (Windows)
Если используется Visual C++, то путь немного другой: на шаге конфигурирования создаётся проект для Visual Studio, который затем можно собрать из IDE либо так же из командной строки.
Созданный проект Visual Studio нельзя изменять и использовать постоянно, потому что при генерации проекта используются абсолютные пути и другие неприемлемые для постоянной работы вещи.
Если проект был сконфигурирован успешно, то в каталоге ../myapp-build появятся автоматически сгенерированный BUILD_ALL.sln и проекты для Visual Studio. Их можно открыть к IDE, либо собрать из командной строки с помощью cmake. Названия опций говорят сами за себя:
Зависимости между библиотеками и приложениями
Не используйте директивы include_directories , add_definitions , add_compile_options ! Они меняют глобальные настройки для всех целей, это создаёт проблемы при масштабировании.
- Используйте target_link_libraries для добавления статических и динамических библиотек, от которых зависит цель
- Используйте target_include_directories вместо include_directories для добавления путей поиска заголовков, от которых зависит цель
- Используйте target_compile_definitions вместо add_definitions для добавления макросов, с которыми собирается цель
- Используйте target_compile_options для добавления специфичных флагов компилятора, с которыми собирается цель
Вы можете выбирать область видимости настройки:
- PUBLIC делает настройку видимой для текущей цели и для всех зависящих от неё целей
- PRIVATE делает настройку видимой только для текущей цели
- INTERFACE делает настройку видимой только для всех зависящих от неё целей
Пример использования областей видимости:
Схема зависимостей условного проекта:
Выбор стандарта и диалекта C++
Для настройки стандарта и флагов языка C++ не добавляйте флаги напрямую!
В CMake версии 3.8+ вы можете прямо потребовать включить нужный стандарт:
В CMake версии до 3.7 включительно можно использовать set_target_properties (если не работает, то у вас слишком старый CMake):
Для разработчиков библиотек есть более тонкий контроль над возможностями языка:
Функции в CMake
CMake позволяет объявлять функции командами function(name) / endfunction() и макросы командами macro(name) / endmacro() . Предпочитайте функции, а не макросы, т.к. у функций есть своя область видимости переменных, а у макросов — нет.
Добавление исходников к цели с target_sources
Лучше добавлять специфичные исходники с помощью target_sources, а не с помощью дополнительных переменных.
Интерфейс к утилитам командной строки
Функция find_package
Функция find_package принимает имя библиотеки как аргумент и обращается к CMake, чтобы найти скрипт для настройки переменных данной библиотеки. В итоге при сборке либо возникает ошибка из-за того что пакет не найден, либо добавляются переменные, хранящие пути поиска заголовков, имена библиотек для компоновщика и другие параметры.
Пример подключения Boost, вызывающего встроенный в CMake скрипт FindBoost:
Пример подключения библиотеки Bullet с помощью встроенного скрипта FindBullet и компоновки с приложением my_app:
Для чего нужен CMake?
Для чего мне может понадобится CMake? Для быстрой компиляции?
5 ответов 5
Утилиты make и Cmake это наследие Unix с тех времен, когда не было IDE, а в качестве терминала была пишущая машинка Консул(!). 🙂 В принципе, Unix-оиды до сих пор тащат эту методологию разработки и построения проектов, как наиболее общую. Иногда без нее не обойтись, но ее доля в реальной жизни снижается. Все работают на IDE и не заморачиваются с make и Cmake.
Вот еще для чего может понадобится Cmake, так это для распространения программ в виде Open Source. Если автор предоставляет Cmake для проекта, то пользователю проще на своей машине перестроить проект. И пользователю не надо в этом случае разбираться где там исходники, где хедеры, где библиотеки и прочее. Если же распространять с проектом не Cmake, а конфигурационный файл для IDE, то во-первых легко перестроить проект получится только с таким же IDE. А во-вторых IDE на машине пользователя должно стоять на том же диске и с теми же путями, что и на машине автора проекта.
то этот пост все равно бы не объяснял — почему?
ТС и не спрашивал «почему». ТС спрашивал «зачем». ТС спрашивал зачем ему да и всем нам нужен CMake, если давно есть удобные IDE. Я попытался ответить зачем сейчас, в 2019 году, может быть нужен CMake. Ничего более убедительного, чем распространение Open Source проектов не вспомнилось.
Ответ же «почему» очевиден — потому что с IDE проще работать. При использовании IDE не надо вручную править конфигурацию CMake каждый раз при добавлении файла исходников в проект. И в IDE все в одном флаконе — и редактор, и отладчик, и компилятор и подсветка синтаксиса и форматирование исходников и подсветка ошибок и профилятор и еще куча всего. Всего этого нет в CMake и вообще всего этого нет в старой методологии разработки через командную строку. Именно поэтому CMake «загнан в темный угол». Именно поэтому 99% программистов работают только с IDE.
Отсутствует этап конфигурации. На сегодняшний день мне неизвестно как в С++ проекте студии указать, что ему требуется VasyanLib, да так чтобы при попытке собрать проект, студия бы спросила меня о местонахождении этой библиотеки, вместо того чтобы плеваться ошибками сборки.
Не критично. 99% проектов обходятся статическим указанием на местоположение библиотек. Если будет критично, то эту функцию добавят в IDE.
Сложно использовать сторонние утилиты в процессе сборки. Например, у меня есть проект, который сначала собирает программу на Хаскелле, запускает ее, а она производит код на С++, который уже превращается в конечную программу. На CMake это заняло строчек 10, а на студии мне неизвестно как это реализовать.
То же самое — не критично. 99% проектов обходятся без сторонних утилит в процессе сборки. Если будет критично, то эту функцию добавят в IDE. Предварительный запуск программы на Хаскелле для написания программы на С++ для последующей трансляции это такая ЭКЗОТИКА о которой и говорить не стоит. Так работают даже не 1% программистов, так работают 0.00001% программистов. Да вобщем-то и сейчас никто не обломится, запустив перед сборкой/после сборки отдельный батник со сторонними утилитами.
Все упомянутые «неудобства» пользования IDE не перевешивают «удобств» пользования IDE. А править вручную конфигурацию для Cmake это то еще удовольствие. Да еще при этом помнить все птичьи правила этого птичьего языка конфигураций.
В общем резюмируя — сборка Unix-style и вообще работа Unix-style времен батьки Денниса Ритчи это для олдскульного поколения, которое успело выучить язык конфигураций make и Cmake до того, как придумали IDE (спасибо за это кстати приснопамятной фирме Borland). Чем дальше, тем меньше остается таких динозавров что и к лучшему. Никто же сейчас не программирует в кодах. А когда-то были специалисты, которые помнили наизусть все коды(!) (даже не мнемоники ассемблера, а КОДЫ) всей системы команд PDP-11. 🙂
Он является генератором сценариев сборки, и таким образом, делает проект переносимым между разными IDE и системами сборки,
Это все очень хорошо, но 99% проектов не нуждаются в переноске между разными IDE и системами сборки. Поэтому это свойство Cmake не востребовано в 99% случаев.
а также позволяет более удобным способом, чем мышеклик, управлять конфигурацией проекта.
Очень сомнительное утверждение.
Основная беда проектных файлов того же вижака — это то, что они отвратно версионируются, а для больших проектов это создает проблемы.
И каким образом использование Cmake помогает версионировать проекты? К тому же для версионирования сейчас используются всякие системы контроля версий типа того же git. Которые тоже ни разу не инструменты командной строки.
так как противопоставлять CMake и IDE неправильно. Это система сборки, которая может использоваться и с IDE.
Может использоваться. А может и не использоваться. И 99% проектов не используют CMake. И 99% программистов не используют CMake. И чем дальше, тем эта цифра будет только расти. А вопрос был от человека, который счастливо живет под IDE и вдруг узнает что есть еще CMake. Естественно у него возникает вопрос — как же он жил до этого и не пользовался CMake и все у него работало? 🙂 А просто время CMake ушло и CMake остался только для старой гвардии, которая умирает но не сдается (С) Наполеон Бонапарт. 🙂
С развитием хороших и разных IDE роль CMake и благословенной командной строки все время снижается. 🙂
Довольно парадоксально выглядит, что ТС вполне удовлетворен ответом, а общественность продолжает негодовать и рассказывать, что собой представляет Cmake. Вопрос был не о том, что собой представляет Cmake, а вопрос был о том, зачем нужен Cmake, если есть IDE, которые умеют все, что умеет Cmake и таким образом IDE делает Cmake не нужным.
Автомобили заменили лошадей, а IDE вполне заменили Cmake. Это называется технический прогресс. Можно и сейчас ездить на телеге, но зачем? Можно и сейчас собирать проекты с помощью Cmake, но зачем?
Ну то есть понятно зачем. Действительно, существует 1% проектов, которые должны быть переносимы между разными IDE и платформами. Для таких проектов, возможно, применение Cmake и оправдано. Но это не мейнстим сейчас. Это глубоко нишевое применение.
Что есть что в CMake 3.10+ и как это использовать
Вступление
Популярность CMake растёт. Многие крупные проекты переходят с собственных инструментов для сборки на CMake. Проект Conan предлагает интеграцию с CMake для управления зависимостями.
Разработчики CMake активно развивают инструмент и добавляют новые функции, решающие общие проблемы, возникающие при сборке проектов. Переход с CMake 2 на CMake 3 был достаточно болезнен. Документация не покрывает все аспекты использования. Функционал крайне обширен, а возникающие трудности различаются от проекта к проекту. В статье я расскажу о инструментах, которые предлагает CMake 3.10 и выше. В каждой новой версии появляются новые детали или улучшаются старые. Об актуальном состоянии лучше проанализировать Changelog, так как многие улучшения последних версий весьма специфичны для отдельных проектов, как например улучшение поддержки Cuda компиляторов. Я же сфокусируюсь на общих понятиях, которые помогут организовать проект на С++ оптимальным образом с точки зрения использования CMake как основной системы сборки.
CMake предлагает широкий набор инструментов. Чтобы не потеряться в определениях, в статье сперва будут определены сущности, через которые будут объяснены конкретные примеры. Названия сущностей я продублирую на английском. Некоторые термины не имеют однозначного перевода на русский язык.
Обзор демонстрационного проекта
Примеры проблем при сборке проекта и рекомендуемые CMake пути решения взяты из моего проекта библиотеки, использующей нейронные сети для определения синхронизации между речью и изображением.
Проект написан на С++, предполагает кросс-платформенность. Содержит переиспользуемые компоненты и зависит от внешних библиотек. Предполагаемая область использования — интеграция в конечное приложение, выполняющее анализ видеопотока, поэтому проект также содержит демонстрационное приложение с примером использования API. Демо-приложение поставляется в исходных кодах вместе с двоичной библиотекой.
Структура файлов и папок проекта с именем ‘neuon’ следующая:
Используемые сущности и понятия
Приложения в комплекте поставки (CMake, CPack, CTest, CDash)
Предопределенные и ожидаемые файлы и их назначение
Объектная модель скриптов — цели, свойства
Свойства, переменные, аргументы и параметры.
Приложения в комплекте поставки (CMake, CPack, CTest, CDash)
CMake поставляет в комплекте с несколькими приложениями, предназначенными для дополнения процесса сборки проекта. За саму сборку отвечает cmake, который работает с файлом, описывающим проект. Использование cmake состоит из нескольких шагов: шаг генерации(generation), шаг сборки(build) и шаг установки(install).
Первым этапом при сборке проекта необходимо сгенерировать скрипты сборки, используя описание проекта(CMakeLists.txt) на языке CMake. Результатом генерации является скрипт или набор файлов, необходимый для запуска нижележащей системы сборки(например Makefile или VIsual Studio Solution). CMake не выполняет запуск компилятора сам, хотя может быть использован как прокси для обобщения вызовов нижележащих инструментов.
Следующим шагом происходит непосредственно сборка проекта из исходного кода. Результатом являются бинарные файлы и другие артефакты, являющиеся частью конечного продукта для потребителя. Все артефакты, несмотря на свою доступность, находятся в специальном состоянии, контролируемом CMake и как правило непригодны к перемещению или распространению, однако, могут быть использованы для отладки локально.
Финальным шагом использования СMake является шаг установки. Установка подразумевает пересборку либо по необходимости перелинковку бинарных артефактов с целью сделать их пригодными к использованию на целевой системе. Как правило, при установке также происходит перемещение артефактов в желаемые расположения и создании желаемой раскладки по директориям. Установка на этом этапе не имеет ничего общего с установкой дистрибутива или распаковкой архива, но при некорректной конфигурации сборки может установить свежесобранные артефакты в локальную систему. При использовании шаг установки не выполняется вручную.
Базовый функционал CMake не заканчивается на консольном приложении. CMake также предоставляет графический интерфейс для управления этапами генерации и сборки — cmake-gui. А также предлагает широкий набор модулей(CMake modules) для использования в файлах описания проекта. Посторонние проекты могут предлагать свои конфигурации(Library Configs) для упрощения использования в связке с CMake.
CMake крайне гибкий и расширяемый инструмент. И цена за это — его перегруженность. Если проекту нужен какой-то функционал при сборке — имеет смысл изучить, что предлагает СMake. Может быть такая же проблема уже была решена и решение прошло проверку сообществом.
Приложение CTest расширяет возможности по сборке путем предоставления единого интерфейса по взаимодействию с тестами. Если проект содержит тесты, ctest играет роль единого запускающего механизма, формирующего отчет по запуску. Для использования CTest как универсального исполнителя каждый тест должен быть зарегистрирован. Согласно его регистрации, в финальный отчет попадет имя и результат выполнения теста. CTest также обеспечивает интеграцию с CDash — панелью общего доступа к результатам запусков тестов, с управлением запусками, группировкой тестов и прочим функционалом, использующимся в автоматизированный конвейерах сборки. (CI/CD Dashboard).
CPack — это инструмент упаковки скомпилированного проекта в платформо-зависимые пакеты и установщики. CPack c одной стороны универсален для создания установщиков целевого формата, с другой стороны зависит от системы, где запускается, так как полагается на системные инструменты для создания установщика. Единый формат командной строки для генерации NSIS установщика для Windows, DEB пакета для Ubuntu и RPM пакета для Centos не подразумевает генерацию RPM при запуске на Ubuntu или Windows. Основным преимуществом CPack перед отдельными упаковщиками является то, что вся конфигурация установщика находится рядом с проектом, и использует те же механизмы, что и сам CMake. Для добавления нового формата в проект достаточно доопределить формато-специфичные переменные и CPack сделает остальную работу.
Предопределенные и ожидаемые файлы и их назначение.
CMake широко использует файловую систему и управляет многими папками и файлами. CMake поддерживает сборку вне расположения проекта. Местоположение проекта называется исходной папкой( Source Directory). При генерации CMake сохраняет файлы в папку сборки (Build Directory). В некоторых контекстах папка сборки также именуется папкой бинарных артефактов(Binary Directory), как противопоставление исходной папке. При выполнении шага установки начинает фигурировать директория установки(Install directory). Эти местоположения могут указывать в одно место, или в разные — это контролируется независимо и доступно при использовании.
Помимо непосредственно файловой системы, CMake использует механизм префиксов для гибкой настройки относительных путей. Префикс установки(( CMAKE_INSTALL_PREFIX )) определяет префикс, который будет использоваться для всех относительных путей, используемых проектом после установки. . Префикс установки и директория установки могут быть одинаковы. Но могут и различаться, что используется для кросс-компиляции или создания переносимых артефактов, не привязанных к конкретному местоположению. Префикс-путь ( CMAKE_PREFIX_PATH ) имеет особенный смысл для шага генерации. Как гласит документация, это список путей, которые СМаке использует в дополнение к своему местоположению как корневую папку для поиска модулей и расширений. При необходимости указать дополнительные места, используется префикс-путь. Он не влияет на результат сборки, однако крайне важен для поиска зависимостей, интеграции со сторонними библиотеками и использования модулей, не входящих в комплект поставки CMake либо являющихся частью проекта.
CMake использует некоторые предопределённые имена или части имени файла, наделяя эти файлы особенным смыслом.
Поддержка CMake в проектах обеспечивается CMakeLists.txt файлом, обычно расположенным в корне проекта. Этих файлов может быть несколько и они могут быть включены друг в друга различными способами. Основная идея этого файла — он предоставляет главную точку входа для CMake и описание проекта начинается с него.
В папке сборки после генерации можно найти файл CMakeCache.txt. Этот файл содержит полное представление всего проекта, которое CMake смог разобрать и сгенерировать. Этот файл может быть отредактирован вручную, для изменения некоторых параметров и переменных. Однако, при следующей генерации изменения могут быть утеряны, если параметры и аргументы запуска CMake утилиты изменились.
Файлы, имена которых регистро-зависимо оканчиваются на Config.cmake или -config.cmake , являются файлами CMake совместимой конфигурации библиотек и зависимостей. (CMake config files). Эти файлы как правило распространяются вместе с библиотеками и обеспечивают интеграции библиотек в проекты использующие CMake для сборки.
Файлы, имена который выглядят как Find*.cmake , содержат CMake модули(CMake modules). Модули расширяют функционал CMake, и используются в CMakeLists.txt для выполнения рутинных задач. Модули следует использовать как фреймворк или библиотеку функций при написании своего CMakeLists.txt
Другие файлы с расширением .cmake предполагают произвольное содержимое написанное с использованием CMake скриптового языка. Проекты включают такие файлы в целях переиспользования частей скриптов.
Иногда в CMake проектах могут встретиться файлы с расширением .in или .in.in Таким образом могут именоваться шаблоны файлов, которые инстанцируются CMake при генерации. Инстанцированные файлы, затем используются проектом как текстовые артефакты, либо как файлы, актуальные в момент генерации и сборки. Например они могут содержать в себе версии и дату сборки бинарных артефактов, или шаблон CMake конфигурации, которая будет распространятся с артефактами позже.
Модули, конфигурации
CMake Модули и CMake конфигурации содержат в себе код на CMake скрипте. Несмотря на схожесть, это разные по назначению сущности. Модули, обычно расширяют поведение CMake, предоставляя функции, макросы и алгоритмы для использования в CMakeLists.txt, поставляются в комплекте с CMake, именуются как Find*.cmake и располагаются в CMAKE_PREFIX_PATH . Модули не привязаны к конкретному проекту и должны быть проектно-независимы. Проект может дополнять список модулей по необходимости при генерации или при использовании, но рекомендуется публиковать модули с целью включить их в состав дистрибутива CMake. Модули играют роль «Стандартной библиотеки CMake». Модули поддерживаются и разрабатываются мейнтейнерами CMake.
Конфигурации же напротив, неотъемлемая часть проекта, поставляются вместе с заголовочными файлами и библиотеками импорта, и обеспечивают интеграцию библиотек в другие проекты. Конфигурация проекта не используется при сборке проекта. Конфигурация является результатом сборки проекта и входит в установщик, поддерживаются и разрабатываются авторами проекта.
Конфигурации не должны располагаться в CMAKE_PREFIX_PATH и должны следовать соглашению о местоположении и именовании, описанном в официальной документации. (https://cmake.org/cmake/help/latest/command/find_package.html#full-signature-and-config-mode)
Конфигурации описывают зависимости проекта, с целью облегчить его использование. Конфигурации крайне чувствительны к своей реализации. Следование практикам и рекомендациям поможет делать конфигурации, которые облегчают интеграции проекта заказчикам и пользователям.
Объектная модель скриптов — цели, свойства
Начиная с версии 3 CMake сместил парадигму своих скриптов с процедурно-ориентированной на объекто-ориентированную. В версии 2, описание проекта содержало набор вызовов функций, которые выполняли настройку свойств, необходимых для сборки и использования. В версии 3, на смену переменным и свойствам пришли цели. Цель(target) в контексте CMake, это некоторая сущность, над которой можно выполнять операции, изменять ее свойства, обеспечивать ее доступность и готовность. Целями могут являются, но не ограничиваются, бинарные артефакты и исполняемые файлы проекта, заголовочные файлы проекта, дополнительные файлы, создаваемые при генерации, зависимости целей, внешние библиотеки или файлы и т.д.
У целей есть свойства(properties), которые могут быть доступны как для чтения(все из них), так и для записи(многие), с целью тонкой настройки и обеспечения желаемого результата. Свойства — это именованные поля целей. Управляются CMake на основе дефолтных значений параметров и аргументов и\или окружения сборки, а также управляются сами проектом, в зависимости от назначения и желаемого результата.
Свойства, переменные, аргументы и параметры.
CMake обеспечивает гибкость настройки и использования предоставляя разные способы контроля и управления своим поведением.
Цели можно контролировать их свойствами. Командная строка при запуске CMake утилит может содержать аргументы командной строки(arguments), передаваемые напрямую в утилиту общепринятым способом —argument или -f lag . В командной строке также могут встречаться параметры(parameters). Параметры, передаваемые в командной строке через -DNAME=VALUE или -DNAME:TYPE=VALUE преобразуются в переменные(variables) с тем же именем в теле скрипта. Также параметрами могут являться переменные окружения или некоторые вхождения CMake переменных в файле CMakeCache.txt. Параметры от переменных в CMakeCache.txt практически ничем не отличаются.
CMake переменные — это переменные в общепринятом смысле слова при написании скриптов. Переменные могут быть объявлены или не быть объявлены. Могут иметь значение или не иметь значение, или иметь пустое значение. Помимо переменных унаследованных от параметров(или значений по умолчанию), в скриптах можно объявлять и использовать свои собственные переменные.
Таким образом, управление CMake утилитами выполняется через аргументы командной строки, параметры запуска, переменными в скриптах, и свойствами целей. Аргументы не оказывают влияние на состояние скриптов, параметры превращаются в переменные с тем же именем, свойства целей имеют предустановленное документированное имя и могут быть установлены в произвольные значения через вызов соответствующих функций.
Примеры задач и использование доступных инструментов
У меня в руках проект, который использует CMake. Что с ним делать?
Сгенерировать сборочные файлы в папке сборки, запустить сборку, опционально запустить установку.
В этом примере мы собираем релизную сборку проекта в отдельной директории. Релиз в данной ситуации подразумевает включение оптимизаций и удаления отладочной информации из бинарного артефакта. CMake имеет 5 известных значений CMAKE_BUILD_TYPE параметра: Debug, Release, RelWithDebInfo, MinSizeRel и None — если не установить конкретное значение. Также мы явно указываем CMAKE_INSTALL_PREFIX /usr/local является значением по умолчанию для Unix систем, а так как запись в эту директорию требует прав суперпользователя, то последняя команда установки вернет ошибку так как не сможет записать файлы по назначению. Следует либо запустить её с правами суперпользователя(что крайне не рекомендуется, если целевая платформа имеет пакетные менеджеры), либо сменить префикс установки в место, не требующее прав суперпользователя, либо не устанавливать проект в систему, либо установить его с использованием переназначения назначения. Для make как нижележащей системы можно использовать DESTDIR: cmake —build cmake-release-build —target install — DESTDIR=$HOME/neuon
Переназначение назначения зависит от собственно системы сборки и не каждая из них умеет такое делать.
Пример сборки под Linux из реального проекта:
Для генерации под Windows, используя Visual Studio:
Как запустить тесты с CTest?
После генерации запустить CTest в папке сборки.
По умолчанию, CTest не выводит ход выполнения тестов. Аргументы вызова помогут достигнуть желаемого поведения.
Как пользоваться целями и свойствами?
Добавляем цель сборки — библиотеку. Не указываем тип библиотеки — разделяемая или статическая, оставляя на усмотрение собирающего. Наш конвейер сборки может собирать два варианта двумя разными вызовами генерации. Затем добавляем цель-псевдоним, это позволяет использовать пространство имен в CMake конфигурации проекта для более выразительного именования зависимостей. Цель-псевдоним используется тут же в проекте демо-приложением, которое при этом умеет использовать и файл конфигурации. Использование пространства имен в файле конфигурации поместит все цели в него. Без псевдонима, демо-приложение должно будет явно отличать связываться с целью родительского проекта, или с целью из конфигурации.
Указываем список библиотек и других зависимостей, с которыми необходимо линковать нашу библиотеку, и выставляем различные свойства, влияющие на параметры компиляции. В последней части, указываем как именно мы хотим устанавливать нашу библиотеку — установка цели описывается через введение экспортируемой сущности, для которой прописываются свойства, такие как папка назначения в зависимости от конкретного типа файла или типа цели или компонент для разделения установщиков на отдельные пакеты или наборы устанавливаемых файлов. Затем указывается установка экспортируемой сущности с указанием пространства имен для используемых целей.
И напоследок несколько файлов устанавливаются как есть. Это ресурсы, которые распространяются как часть проекта.
Как использовать файлы конфигурации?
Начиная с CMake 3.0 рекомендуется в файлах конфигурации объявлять цели, которые могут быть использованы потребителем. До CMake 3.0 файлы конфигурации объявляли несколько переменных, именованных по определенным правилам и эти переменные становились доступны потребителю. На настоящий момент, некоторые проект до сих пор используют только старый подход, некоторые проекты используют старый подход как дополнение к новому. Иногда появляются инициативы завернуть старые конфигурации в новые или в CMake модули. В этом примере мы уделяем внимание новому подходу, как рекомендуемому.
find_package ищет файлы конфигурации, руководствуясь предопределенными правилами разрешения путей. Если этот CMakeLists.txt не включен в основной проект, то neuon::neuon цель будет недоступна и нам требуется явно подключить библиотеку штатным способом. В обратном случае — цель-псевдоним обеспечит нам идентичный функционал и наше приложение будет слинковано с библиотекой из той же директории сборки.
Мы заранее ввели цель-псевдоним в основном проекте библиотеки для универсальности нашего CMakeLists.txt в проекте демо-приложения. Теперь при сборке нашего демо-приложения как части сборки проекта — будет использоваться цель-псевдоним, а при сборке пользователем наша библиотека будет доступна через имя определенное в конфигурации, дополненное пространством имён.
Как добавить свои тесты в запуск CTest?
enable_testing() указывает CMake, что планируется использование CTest. add_test() регистрирует исполняемый файл, собираемый в проекте как один из тестов для запуска. Тестом может быть любая исполняемая сущность — приложение, скрипт, сторонний инструмент. CTest опирается на код возврата для определения пройден тест или нет и формирует соответственный отчет.
CMake предлагает готовый модуль для работы с Google Test Framework. Если ваши тесты используют Googletest, то в исполняемом файле обычно множество юнит-тестов. Регистрация штатным способом не даст полной картины запуска юнит-тестов, так как с точки зрения CMake — одно приложение, один тест. include(GoogleTest) подключает стандартный модуль, который содержит функцию gtest_discover_tests , которая регистрирует все тесты из собранного тестового приложения как отдельные тесты в CTest. Отчет становится гораздо более информативным.
Как конфигурировать CPack?
Подключив требуемые модули расширяющие CPack, необходимо выставить значения задокументированных переменных для каждого конкретного формата установщика или пакета. Некоторые значения берутся из переменных и параметров CMake, некоторые могут быть заполнены автоматически при выполнение упаковки. После выставления переменных следует подключить собственно модуль CPack через include(CPack).
Как написать конфигурацию для своего проекта?
По созданию своего файла конфигурации есть статья здесь.
В общих чертах, файл конфигурации описывает цели, которые пользователь может использовать в своем проекте. Список целей генерируется CMake при создании экспортируемой сущности, привязанной к целям. Версионирование и проверки целостности конфигурации общего назначения реализованы в модуле CMakePackageConfigHelpers.
Шаблон конфигурации может быть похож на:
Как использовать конфигурации других проектов?
Корректно подготовленная конфигурация не требует никаких дополнительных действия для использования.
Конфигурация декларирует какие библиотеки доступны для использования, какие дополнительные зависимости необходимо слинковать с конечным результатом, какие флаги компилятора должны быть выставлены при использовании. Где находятся заголовочные файлы, и библиотеки импорта тоже указывается посредством файла конфигурации и избавляет пользователя от ручного труда.
Как использовать стандартные CMake модули?
Несмотря на регламентированное именование модулей использование их в своих проектах двоякое. Многие стандартные модули CMake подключаются через функцию include(), однако многие модули, выполняющие поиск библиотек, не поддерживающих CMake самостоятельно через конфигурацию полагаются на find_package() в режиме модулей.
Библиотека Boost начиная с версии 1.70 обеспечивает поддержку CMake через конфигурации. СMake модуль обладает обратной совместимостью и умеет разрешать местонахождение Boost любой версии, используя конфигурации при наличии и создавая цели-псевдонимы в противном случае.
Как использовать модули из посторонних или личных источников?
Нередки случаи, когда стандартного модуля нет в наличии, или его состояние не удовлетворяет нуждам проекта. В таком случае проект может предоставить свои собственные CMake модули, для использования в своих конфигурациях. Ключевым моментом для использования собственных модулей является параметр CMAKE_MODULE_PATH , перечисляющий пути поиска модулей. Проект или конфигурация проекта могут самостоятельно изменять переменную, выведенную из этого параметра. Например, можно добавить в свой проект модуль, выполняющий заворачивание всех нееобходимых флагов компиляции для использования FFMpeg в цели CMake и связывать свое приложение с этими целями, не беспокоясь о флагах линковки, флагах компилятора и всех зависимостях.
При изменении CMAKE_MODULE_PATH в своих конфигурациях, чтобы избежать конфликтов модулей с проектом пользователя, можно использовать функции по работе со списками. После добавления своих путей в переменную и использования, можно удалить эти вхождения из списка.
Как подключать и переиспользовать другие куски CMake скриптов?
CMake позволяет включать части скриптов друг в друга как есть, не выполняя дополнительной работы. Это может быть полезно для переиспользования функционала, когда использование модуля не обосновано, а дублирования хочется избежать. include() включит содержимое указанной файла в использующий файл. Включаемые файлы должны быть реентерабельны, и не изменять глобальное состояние.
Заключение
CMake 3.10+ однозначно полагается на объектную модель использования. Все есть цель. У цели есть свойства. Проект не должен изменять глобальное состояние. Проект описывает свои цели, и использует свойства целей для модификации своего поведения. Модули — расширяют функционал CMake. Конфигурации обеспечивают интерфейс интеграции проектов и зависимостей. Переиспользование функционала доступно и рекомендуется.
Глобальные свойства, параметры и переменные управляются CMake. Проект не должен их менять по своем усмотрению. Проект должен подстраиваться под глобальное состояние.
Сборка настраивается и модифицируется не внутри скриптов, а используя параметры и аргументы при запуске CMake. Позвольте будущему себе как автору проекта решать, разделяемая библиотека или статическая, установка в системные папки или в директории пользователя с ограниченными правами, включить максимальную оптимизацию или обеспечить отладочную информацию.
CMake предлагает очень широкий выбор доступных параметров и переменных, не стоит дублировать их в своем проекте.
Использование относительных путей предпочтительнее абсолютных.
CMake проекты могут быть включены друг в друга в произвольном порядке. Один проект не должен разрушать состояние, созданное другим.
CMake распространяется как самодостаточный архив под все популярные платформы. Файл конфигурации ограничен версией CMake потребителя, но для сборки и упаковки проекта можно притащить последнюю версию, развернуть локально как часть окружения сборки и пользоваться наиболее свежим функционалом, которые сделан, чтобы делать жизнь авторов проектов проще.
Полезные ссылки
В чем разница между разными способами включения подпроектов?
CMake предлагает разные способы подключения проектов друг в друга. Если допустить, что проект описывается как минимум одним CMakeLists.txt, то проекты между собой могут быть связаны через add_subdirectory(), include(), ExternalProject_Add().
Основная идея в том, что CMakeLists.txt описывает проект как нечто именованное и версионированное. Директива project() устанавливает некоторые переменные, используемые CMake при генерации и сборке. Как минимум версию проекта.
include() включает содержимое как есть, а в одном CMakeLists.txt не может оказаться две активные директивы project().
ExternalProject_Add() предлагает способ сборки подпроекта из исходного кода, но не делает подпроект частью сборки основного проекта. Через ExternalProject_Add обычно подключаются сторонние зависимости, которые не могут быть удовлетворены окружением сборки или целевой системой.
add_subdirectory() единственный корректный способ подключить проект в проект, создав единое дерево зависимостей и связей между целями, сохранив при этом проектную идентичность и самодостаточность. Помимо непосредственно подключения проекта, CMake выполняет дополнительные операции по изоляции подпроектов и областей видимости.