source dir что это
C++ и CMake — братья навек, часть II
В предыдущей части данного занимательного рассказа говорилось об организации заголовочной библиотеки в рамках генератора систем сборки CMake.
В этот раз добавим к нему компилируемую библиотеку, а также поговорим о компоновке модулей друг с другом.
Как и прежде, тем, кому не терпится, могут сразу перейти в обновлённый репозиторий и потрогать всё своими руками.
Содержание
Разделяй
Первое, что нужно сделать для достижения нашей высокой цели — это разделить разрабатываемое ПО на универсальные изолированные блоки, единообразные с точки зрения пользователя.
В первой части был описан такой стандартный блок — проект с заголовочной библиотекой. Теперь же добавим в наш проект компилируемую библиотеку.
Далее сделаем, чтобы новая библиотека тоже устанавливалась в систему:
После этого осталось провязать модульные тесты с новой библиотекой (функцию myfunc вынесли из заголовка, так что теперь нужно линковаться):
Заголовки ( Mylib::mylib ) теперь отдельно подключать не нужно, потому что, как уже было сказано, они автоматически подключаются вместе с библиотекой ( Mylib::myfeature ).
И добавим пару нюансов, чтобы обеспечить замеры покрытия с учётом новоприбывшей библиотеки:
Можно добавить больше библиотек, исполняемые файлы и т.д. При этом не важно, как именно они провязаны между собой в рамках проекта. Важно только то, какие цели являются интерфейсом нашего модуля, то есть торчат наружу.
Властвуй
Теперь у нас есть стандартные модули-блоки, и мы можем властвовать над ними: составлять из них структуру любой сложности, устанавливая их в систему или связывая между собой в рамках единой системы сборки.
Установка в систему
Один из вариантов использования модуля — установить наш модуль в систему.
Подключение в качестве подмодуля
Использование
CMake-цели могут быть хитрыми, например, предназначенными только для того, чтобы пробрасывать какие-либо свойства, зависимости и т.п. При этом работа с ними происходит единым образом.
Русские Блоги
CMake установка и использование
Установка и новое строительство (Ubuntu)
Найдите место, чтобы разархивировать и войти.
Согласно README Файл, выполните следующую команду для установки.
По умолчанию CMake будет установлен в / usr / local / bin.
Скомпилируйте и установите CMake с CMake
Создайте простой проект CMake
Создайте новую папку как папку проекта, создайте новый файл CMakeLists.txt Ниже приведена типичная конфигурация:
GitHub отличная ссылка на проект CMake
Поддержка Visual Studio для CMake
Синтаксис CMake
Грамматические особенности
Оператор присваивания
Условные заявления
функция
макрос
Встроенные функции
Встроенные переменные
CMAKE_SOURCE_DIR | PROJECT_SOURCE_DIR | CMAKE_CURRENT_SOURCE_DIR |
Все указывают на абсолютный путь к исходному каталогу проекта ( CMakeLists.txt Где абсолютный путь).
CMAKE_SOURCE_DIR Укажите текущий проект верхнего уровня (родительский проект) CMakeLists.txt маршрут.
PROJECT_SOURCE_DIR Указать текущий проект (подпроект) CMakeLists.txt маршрут.
CMAKE_CURRENT_SOURCE_DIR Укажите, что в настоящее время обрабатывается CMakeLists.txt Путь (чувствую и PROJECT_SOURCE_DIR Нет разницы. ).
_SOURCE_DIR Это указать элемент CMakeLists.txt Путь. Но до вызова проекта переменная не определена.
Поэтому, когда нет подпроектов, они одинаковы.
Например, для родительского проекта Outer С подпроектом Inner :
В Outer/CMakeLists.txt Значения переменных в:
CMAKE_SOURCE_DIR | PROJECT_SOURCE_DIR | CMAKE_CURRENT_SOURCE_DIR | Outer_SOURCE_DIR | Inner_SOURCE_DIR |
---|---|---|---|---|
/. /Outer | /. /Outer | /. /Outer | /. /Outer | /. /Outer/Inner |
В Outer/Inner/CMakeLists.txt Значения переменных в:
CMAKE_SOURCE_DIR | PROJECT_SOURCE_DIR | CMAKE_CURRENT_SOURCE_DIR | Outer_SOURCE_DIR | Inner_SOURCE_DIR |
---|---|---|---|---|
/. /Outer | /. /Outer/Inner | /. /Outer/Inner | /. /Outer | /. /Outer/Inner |
TODO: PROJECT_SOURCE_DIR против CMAKE_CURRENT_SOURCE_DIR различия.
CMAKE_BINARY_DIR | PROJECT_BINARY_DIR | CMAKE_CURRENT_BINARY_DIR |
Укажите абсолютный путь, по которому происходит компиляция проекта. Разница та же, что и выше.
Если он скомпилирован в исходном коде, он ссылается на каталог верхнего уровня проекта, то есть _SOURCE_DIR То же самое. И если это так:
Ступай на яму
Отображение зависимости проекта (graphviz)
Создайте файл графа зависимостей:
CMake и C++ — братья навек
В процессе разработки я люблю менять компиляторы, режимы сборки, версии зависимостей, производить статический анализ, замерять производительность, собирать покрытие, генерировать документацию и т.д. И очень люблю CMake, потому что он позволяет мне делать всё то, что я хочу.
Многие ругают CMake, и часто заслуженно, но если разобраться, то не всё так плохо, а в последнее время очень даже неплохо, и направление развития вполне позитивное.
В данной заметке я хочу рассказать, как достаточно просто организовать заголовочную библиотеку на языке C++ в системе CMake, чтобы получить следующую функциональность:
Кто и так разбирается в плюсах и си-мейке может просто скачать шаблон проекта и начать им пользоваться.
Содержание
Проект изнутри
Структура проекта
Главным образом речь пойдёт о том, как организовать CMake-скрипты, поэтому они будут разобраны подробно. Остальные файлы каждый желающий может посмотреть непосредственно на странице проекта-шаблона.
Главный CMake-файл (./CMakeLists.txt)
Информация о проекте
В первую очередь нужно затребовать нужную версию системы CMake. CMake развивается, меняются сигнатуры команд, поведение в разных условиях. Чтобы CMake сразу понимал, чего мы от него хотим, нужно сразу зафиксировать наши к нему требования.
Затем обозначим наш проект, его название, версию, используемые языки и прочее (см. команду project ).
В данном случае указываем язык CXX (а это значит C++), чтобы CMake не напрягался и не искал компилятор языка C (по умолчанию в CMake включены два языка: C и C++).
Здесь же можно сразу проверить, включён ли наш проект в другой проект в качестве подпроекта. Это сильно поможет в дальнейшем.
Опции проекта
Предусмотрим две опции.
Первая опция — MYLIB_TESTING — для выключения модульных тестов. Это может понадобиться, если мы уверены, что с тестами всё в порядке, а мы хотим, например, только установить или запакетировать наш проект. Или наш проект включён в качестве подпроекта — в этом случае пользователю нашего проекта не интересно запускать наши тесты. Вы же не тестируете зависимости, которыми пользуетесь?
Кроме того, мы сделаем отдельную опцию MYLIB_COVERAGE для замеров покрытия кода тестами, но она потребует дополнительных инструментов, поэтому включать её нужно будет явно.
Опции компиляции
Разумеется, мы крутые программисты-плюсовики, поэтому хотим от компилятора максимального уровня диагностик времени компиляции. Ни одна мышь не проскочит.
Расширения тоже отключим, чтобы полностью соответствовать стандарту языка C++. По умолчанию в CMake они включены.
Основная цель
Наша библиотека состоит только из заголовочных файлов, а значит, мы не располагаем каким-либо выхлопом в виде статических или динамических библиотек. С другой стороны, чтобы использовать нашу библиотеку снаружи, её нужно установить, нужно, чтобы её можно было обнаружить в системе и подключить к своему проекту, и при этом вместе с ней были привязаны эти самые заголовки, а также, возможно, какие-то дополнительные свойства.
Для этой цели создаём интерфейсную библиотеку.
Привязываем заголовки к нашей интерфейсной библиотеке.
Установим стандарт языка. Разумеется, самый последний. При этом не просто включаем стандарт, но и распространяем его на тех, кто будет использовать нашу библиотеку. Это достигается за счёт того, что установленное свойство имеет категорию INTERFACE (см. команду target_compile_features).
Заводим псевдоним для нашей библиотеки. Причём для красоты он будет в специальном «пространстве имён». Это будет полезно, когда в нашей библиотеке появятся разные модули, и мы заходим подключать их независимо друг от друга. Как в Бусте, например.
Установка
Установка наших заголовков в систему. Тут всё просто. Говорим, что папка со всеми заголовками должна попасть в директорию include относительно места установки.
Тесты
Документация
Документация также не будет генерироваться в случае подпроекта.
Онлайн-песочница
Аналогично, онлайн-песочницы у подпроекта тоже не будет.
Скрипт для тестов (test/CMakeLists.txt)
Тестирование
Первым делом находим пакет с нужным тестовым фреймворком (замените на свой любимый).
А файлы, в которых описаны сами тесты, добавляю позже. Но так делать не обязательно.
Наконец, создаём фиктивную цель, «сборка» которой эквивалентна запуску тестов, и добавляем эту цель в сборку по умолчанию (за это отвечает атрибут ALL ). Это значит, что сборка по умолчанию инициирует запуск тестов, то есть мы никогда не забудем их запустить.
Покрытие
Скрипт для документации (doc/CMakeLists.txt)
Дальше проверяем, установлена ли пользователем переменная с языком. Если да, то не трогаем, если нет, то берём русский. Затем конфигурируем файлы системы Doxygen. Все нужные переменные, в том числе и язык попадают туда в процессе конфигурации (см. команду configure_file ).
Скрипт для онлайн-песочницы (online/CMakeLists.txt)
Проект снаружи
Теперь рассмотрим, как этим всем пользоваться.
Сборка
Сборка данного проекта, как и любого другого проекта на системе сборки CMake, состоит из двух этапов:
Генерация
Сборка проекта
Опции
MYLIB_COVERAGE
MYLIB_TESTING
MYLIB_DOXYGEN_LANGUAGE
Переключает язык документации, которую генерирует цель doc на заданный. Список доступных языков см. на сайте системы Doxygen.
По умолчанию включён русский.
Сборочные цели
По умолчанию
mylib-unit-tests
Компилирует модульные тесты. Включено по умолчанию.
check
Запускает собранные (собирает, если ещё не) модульные тесты. Включено по умолчанию.
coverage
Анализирует запущенные (запускает, если ещё не) модульные тесты на предмет покрытия кода тестами при помощи программы gcovr.
Выхлоп покрытия будет выглядеть примерно так:
Запускает генерацию документации к коду при помощи системы Doxygen.
wandbox
Ответ от сервиса выглядит примерно так:
Для этого используется сервис Wandbox. Не знаю, насколько у них резиновые сервера, но думаю, что злоупотреблять данной возможностью не стоит.
Примеры
Сборка проекта в отладочном режиме с замером покрытия
Установка проекта без предварительной сборки и тестирования
Сборка в выпускном режиме заданным компилятором
Генерирование документации на английском
Инструменты
На самом деле версия CMake 3.13 требуется только для запуска некоторых консольных команд, описанных в данной справке. С точки зрения синтаксиса CMake-скриптов достаточно версии 3.8, если генерацию вызывать другими способами.
Тестирование можно отключать (см. опцию MYLIB_TESTING ).
Для автоматической генерации онлайн-песочницы.
Статический анализ
С помощью CMake и пары хороших инструментов можно обеспечить статический анализ с минимальными телодвижениями.
Cppcheck
В CMake встроена поддержка инструмента для статического анализа Cppcheck.
Для этого нужно воспользоваться опцией _CPPCHECK»> CMAKE_CXX_CPPCHECK :
После этого статический анализ будет автоматически запускаться каждый раз во время компиляции и перекомпиляции исходников. Ничего дополнительного делать не нужно.
Clang
При помощи чудесного инструмента scan-build тоже можно запускать статический анализ в два счёта:
Послесловие
CMake — очень мощная и гибкая система, позволяющая реализовывать функциональность на любой вкус и цвет. И, хотя, синтаксис порой оставляет желать лучшего, всё же не так страшен чёрт, как его малюют. Пользуйтесь системой сборки CMake на благо общества и с пользой для здоровья.
Современный CMake: 10 советов по улучшению скриптов сборки
CMake — это система сборки для C/C++, которая с каждым годом становится всё популярнее. Он практически стал решением по умолчанию для новых проектов. Однако, множество примеров выполнения какой-либо задачи на CMake содержат архаичные, ненадёжные, раздутые действия. Мы выясним, как писать скрипты сборки на CMake лаконичнее.
Если вы хотите опробовать советы в деле, возьмите пример на github и исследуйте его по мере чтения статьи: https://github.com/sergey-shambir/modern-cmake-sample
Совет №1: указывайте высокую минимальную версию CMake
Совет не относится к тем, кто пишет публичные библиотеки, поскольку для них важна совместимость со старым окружением разработки. А если вы пишете проект с закрытым кодом либо узкоспециальное опенсорсное ПО, то можно потребовать от всех разработчиков поставить последнюю версию CMake. Без этого многие советы статьи работать не будут! На момент написания статьи мы имеем CMake 3.8.
Совет №2: не вызывайте ни make, ни make install
Современный CMake умеет сам вызывать систему сборки. В документации CMake такой режим называется Build Tool Mode.
Если вы генерируете проект Visual Studio, вы также можете собрать его из командной строки, в том числе можно собрать конкретный проект в конкретной конфигурации:
На Linux не используйте make install, иначе вы засорите свою систему. Об этом есть отдельная статья Хочется взять и расстрелять, или ликбез о том, почему не стоит использовать make install
Совет №3: используйте несколько CMakeLists.txt
Совет №4: не засоряйте глобальную область видимости
Есть пример схемы зависимостей, взятый из презентации Modern CMake / an Introduction за авторством Tobias Becker:
Совет №5: включите, наконец, C++17 или C++14!
В последние годы стандарт C++ обновляется часто: мы получили потрясающие изменения в C++11, C++14, C++17. Старайтесь по возможности отказаться от старых компиляторов. Например, для Linux ничто не мешает установить последнюю версию Clang и libc++ и начать собирать все проекты со статической компоновкой C++ runtime.
Лучший способ включить C++17 без игры с флагами компиляции — явно сказать CMake, что он вам нужен.
С помощью target_compile_features вы можете требовать не C++17 или C++14, а определённых фич со стороны компилятора. Полный список известных CMake фич компиляторов можно посмотреть в документации.
Совет №6: используйте функции
В CMake можно объявлять свои функциональные макросы и свои функции. Есть лишь одно различие между ними: переменные, установленные внутри функции, являются локальными.
Удобно писать функции, чтобы решать текущие проблемы кастомизации сборки либо упрощать добавление множества целей сборки. Пример ниже был написан для более корректного включения C++17 из-за того, что
Каждая функция — это по сути хак, созданный для переопределения языка CMake или его поведения. Для других разработчиков смысл этого хака неясен. Поэтому старайтесь к каждой инструкции в функции добавлять комментарий, объясняющий её цель и смысл.
В крупных открытых проектах, например в KDE, применение своих функций может быть дурным тоном. Вы можете рассмотреть иные варианты: писать скрипт сборки явно по принципу «Explicit is better then implicit», либо даже предложить добавить свою функцию в upstream проекта CMake.
Совет №7 (спорный): не перечисляйте исходные файлы по одному
Мой коллега разрабатывает вне работы маленький 3D движок для рендеринга сцены с моделями и анимациями через OpenGL, GLES, DirectX и Vulkan. Однажды мы с ним обсуждали этот проект, и оказалось, что для сборки под все платформы (Windows, Linux, Android) он использует Visual Studio! Он недоволен тем, что Microsoft редко обновляет Android NDK, но не хочет отказываться от сборки через MSBuild по одной простой причине.
Ему не хочется сопровождать список файлов для сборки в двух системах сборки.
Вы можете добавить функцию custom_add_library_from_dir для целей-библиотек аналогичным путём.
Если же вы — фанат ручной работы или создаёте публичную библиотеку, тогда, возможно, вам лучше добавлять файлы по одному. В этом случае используйте target_sources для добавления платформо-специфичных файлов:
Совет №9: оставляйте больше гибкости пользователям своих библиотек
Совет относится к вам, если вы пишете публично доступные библиотеки. В этом случае вам стоит упрощать следующие сценарии:
Добавляя библиотеку, создавайте ещё и уникальный синоним:
Оставляйте пользователям библиотеки право использования опции BUILD_SHARED_LIBS для выбора между сборкой статической и динамической версий библиотеки.
При установке настроек компоновки, поиска заголовков и флагов компиляции для библиотек используйте ключевые слова PUBLIC, PRIVATE, INTERFACE, чтобы позволить целям, зависящим от вашей библиотеки, наследовать необходимые настройки:
Совет №10: регистрируйте автотесты в CTest
Чтобы включить поддержку CTest по всему проекту, есть инструкция enable_testing
Что ещё почитать?
Если для своего OpenSource проекта вы настроили CMake и CTest, дальше можете подключить непрерывную интеграцию: Непрерывная интеграция (CI) для GitHub проектов на С/C++ с CMake-сборкой
Перед созданием статьи были прочитаны, опробованы и переосмыслены несколько англоязычных источников:
Некоторые советы из этих источников никак не отражены в статье. Поэтому после их прочтения вы определённо станете глубже разбираться в CMake.
Полное руководство по CMake. Часть вторая: Система сборки
Введение
В данной статье рассмотрено использование системы сборки CMake, применяемой в колоссальном количестве проектов на C/C++. Строго рекомендуется прочитать первую часть руководства во избежание непонимания синтаксиса языка CMake, явным образом фигурирующего на протяжении всей статьи.
Запуск CMake
Ниже приведены примеры использования языка CMake, по которым Вам следует попрактиковаться. Экспериментируйте с исходным кодом, меняя существующие команды и добавляя новые. Чтобы запустить данные примеры, установите CMake с официального сайта.
Принцип работы
Система сборки CMake представляет из себя оболочку над другими платформенно зависимыми утилитами (например, Ninja или Make). Таким образом, в самом процессе сборки, как бы парадоксально это ни звучало, она непосредственного участия не принимает.
Система сборки CMake принимает на вход файл CMakeLists.txt с описанием правил сборки на формальном языке CMake, а затем генерирует промежуточные и нативные файлы сборки в том же каталоге, принятых на Вашей платформе.
Сгенерированные файлы будут содержать конкретные названия системных утилит, директорий и компиляторов, в то время как команды CMake орудуют лишь абстрактным понятием компилятора и не привязаны к платформенно зависимым инструментам, сильно различающихся на разных операционных системах.
Проверка версии CMake
Команда cmake_minimum_required проверяет запущенную версию CMake: если она меньше указанного минимума, то CMake завершает свою работу фатальной ошибкой. Пример, демонстрирующий типичное использование данной команды в начале любого CMake-файла:
Как подметили в комментариях, команда cmake_minimum_required выставляет все флаги совместимости (смотреть cmake_policy ). Некоторые разработчики намеренно выставляют низкую версию CMake, а затем корректируют функционал вручную. Это позволяет одновременно поддерживать древние версии CMake и местами использовать новые возможности.
Оформление проекта
В начале любого CMakeLists.txt следует задать характеристики проекта командой project для лучшего оформления интегрированными средами и прочими инструментами разработки.
Запуск скриптовых файлов
Команда include заменяет строку своего вызова кодом заданного файла, действуя аналогично препроцессорной команде include языков C/C++. Этот пример запускает скриптовый файл MyCMakeScript.cmake описанной командой:
Компиляция исполняемых файлов
Компиляция библиотек
Добавление исходников к цели
Повторяющиеся вызовы команды target_sources добавляют исходные файлы к цели в том порядке, в каком они были вызваны, поэтому нижние два блока кода являются функционально эквивалентными:
Генерируемые файлы
Важно подметить, что «DLL-платформами» считаются все платформы, основанные на Windows, в том числе и Cygwin.
Компоновка с библиотеками
Стоит отметить, что модульные библиотеки не подлежат компоновке с исполняемыми файлами или другими библиотеками, так как они предназначены исключительно для загрузки техниками выполнения.
Работа с целями
Как упомянули в комментариях, цели в CMake тоже подвержены ручному манипулированию, однако весьма ограниченному.
Имеется возможность управления свойствами целей, предназначенных для задания процесса сборки проекта. Команда get_target_property присваивает предоставленной переменной значение свойства цели. Данный пример выводит значение свойства C_STANDARD цели MyTarget на экран:
Пример выше задал цели MyTarget свойства, влияющие на процесс компиляции, а именно: при компиляции цели MyTarget CMake затребует компилятора о использовании стандарта C11. Все известные именования свойств целей перечисляются на этой странице.
Также имеется возможность проверки ранее определённых целей с помощью конструкции if(TARGET ) :
Добавление подпроектов
Команда add_subdirectory побуждает CMake к незамедлительной обработке указанного файла подпроекта. Пример ниже демонстрирует применение описанного механизма:
Стоит отметить, что все переменные из родительской области видимости унаследуются добавленным каталогом, а все переменные, определённые и переопределённые в данном каталоге, будут видимы лишь ему (если ключевое слово PARENT_SCOPE не было определено аргументом команды set ). Данную особенность упомянули в комментариях к предыдущей статье.
Поиск пакетов
Команда find_package находит и загружает настройки внешнего проекта. В большинстве случаев она применяется для последующей линковки внешних библиотек, таких как Boost и GSL. Данный пример вызывает описанную команду для поиска библиотеки GSL и последующей линковки:
В общем случае, команда find_package имеет две разновидности запуска: модульную и конфигурационную. Пример выше применял модульную форму. Это означает, что во время вызова команды CMake ищет скриптовый файл вида Find
Способы включения заголовков
Команда target_include_directories влияет лишь на указанную первым аргументом цель, а на другие цели никакого воздействия не оказывается. Пример ниже демонстрирует разницу между этими двумя командами:
Установка проекта
Команда install генерирует установочные правила для Вашего проекта. Данная команда способна работать с целями, файлами, папками и многим другим. Сперва рассмотрим установку целей.
После завершения обработки CMake всех Ваших файлов Вы можете выполнить установку всех описанных объектов командой sudo checkinstall (если CMake генерирует Makefile ), или же выполнить данное действие интегрированной средой разработки, поддерживающей CMake.
Наглядный пример проекта
Данное руководство было бы неполным без демонстрации реального примера использования системы сборки CMake. Рассмотрим схему простого проекта, использующего CMake в качестве единственной системы сборки:
Заключение
Теперь Вы способны писать свои и понимать чужие CMake-файлы, а подробно прочитать про остальные механизмы Вы можете на официальном сайте.
Следующая статья данного руководства будет посвящена тестированию и созданию пакетов с помощью CMake и выйдет через неделю. До скорых встреч!