Довелось мне работать с проектом представляющий криптографическую библиотеку — engine для OpenSSL. Эта библиотека помимо реализации криптографических алгоритмов содержит дополнительные компоненты:
-
Модуль самотестирования
-
Модуль проверки лицензии
-
Криптографические контейнеры
-
Генератор случайных данных
Каждый вышеописанный компонент может быть отключен в моменте компиляции.
Скрытый текст
Такая модульность достигается за счет передаваемых команде конфигурации аргументов. При установленном аргументе происходит проброс соответствующего макроса, который на этапе препроцессинга, меняет исходный код в соответствии с указанным макросом.
Так например при кросскомпиляции физическая случайность с использованием rdrand поддерживается не на всех семействах процессоров и в связи с этим необходимо держать каждую конфигурацию сборки в голове или bash скрипте, пока на помощь не придут CMake пресеты.
Пресеты — это не только вынесение опций за скобки.
Перед тем как пощупать данную технологию было изучено пару статей, очень понравилась эта статья. Большинство примеров демонстрируют достаточно тривиальные проекты, где применение пресетов ограничивается разделением на debug, release и release with debug info. В этой статье будет рассмотрено применение пресетов в боевом проекте, в котором присутствует принадлежность к разным программным продуктам, целевым системам и архитектурам сборки, а также наличие модульных компонентов.
Модульность библиотеки достигается наличием в CMakeLists.txt условных конструкций, проверяющих определенные флаги, вынести эту логику за скобки пресеты не могут, но они позволят зафиксировать всевозможные комбинации используемых компонентов.
Поэтому вместо
# Первый вариант регулирования подключаемых модулей к проекту cmake -D<unit_name_flag>=ON -D<unit_name_flag>=OFF # Второй вариант регулирования подключаемых модулей к проекту cmake -D<unit_name_flag>=OFF -D<unit_name_flag>=ON
достаточно вызвать команду
# Первый вариант регулирования подключаемых модулей к проекту cmake --preset <preset_1> # Второй вариант регулирования подключаемых модулей к проекту cmake --preset <preset_2>
а в случае чрезмерного количества пресетов всегда можно вызвать команду
cmake --list-presets
Для такой модульной библиотеки отлично подходит механизм наследования пресетов. Начнем с вынесения за скобки базовых опций
{ "version": 6, "configurePresets": [ { "name": "base", "hidden": true, "cacheVariables": { "CMAKE_BUILD_RPATH": "./", "CMAKE_CXX_STANDRAD": "11", "CMAKE_CXX_STANDARD_REQUIRED": { "type": "BOOL", "value": "ON" }, "CMAKE_POSITION_INDEPENDENT_CODE": { "type": "BOOL", "value": "ON" }, "LINK_<unit_name>": { "type": "BOOL", "value": "ON" }, // Локальные переменные используемые в CMakeLists.txt "LINK_<unit_name>": { "type": "BOOL", "value": "OFF" }, //... } } ] }
Следующим шагом были определены пресеты для различных операционных систем и архитектур процессора.
Пресет для Linux систем получился достаточно простым, т.к. содержит только флаг отвечающий за флаг сборки OpenSSL.
Пресеты для кросскомпиляции под Windows выглядят следующим образом:
{ "version": 6, "configurePresets": [ { "name": "win", "hidden": true, "cacheVariables":{ "CMAKE_C_FLAGS": "-static-libgcc -static-libstdc++", "CMAKE_CXX_FLAGS": "-static-libgcc -static-libstdc++", "CMAKE_SHARED_LINKER_FLAGS": "-static-libgcc -static-libstdc++", "CMAKE_EXE_LINKER_FLAGS": "-static-libgcc -static-libstdc++", "CMAKE_SYSTEM_NAME": "Windows" } }, { "name": "win64", "hidden": true, "inherits": ["win"], "cacheVariables":{ "OSSL_ARCH": "WIN64", "CMAKE_C_COMPILER": "x86_64-w64-mingw32-gcc", "CMAKE_CXX_COMPILER": "x86_64-w64-mingw32-g++" } }, { "name": "win32", "hidden": true, "inherits": ["win"], "cacheVariables":{ "OSSL_ARCH": "WIN32", "CMAKE_C_COMPILER": "i686-w64-mingw32-gcc", "CMAKE_CXX_COMPILER": "i686-w64-mingw32-g++" } } ] }
Здесь уже можно увидеть простейший вариант наследования в действии, где отличительной чертой для 32 и 64 битных версий выступает используемый компилятор и флаг для OpenSSL.
Для модульных компонентов можно было выбрать два пути — первый заключается в стандартизированном создании двух пресетов на включение и отключение, а второй вариант заключается в разделении на две группы:
-
Обязательные компоненты, но необходимые к отключению в исключительных случаях
-
Необязательные компоненты для минимального функционирования библиотеки
Исходя из базового пресета можно заметить, что был выбран второй вариант. Таким образом выглядит пресет отвечающий за компонент самотестирования:
{ "version": 6, "configurePresets": [ { "name": "enable-self-test", "hidden": true, "cacheVariables": { "LINK_self_test": { "type": "BOOL", "value": "ON" } } }, { "name": "disable-self-test", "hidden": true, "cacheVariables": { "LINK_self_test": { "type": "BOOL", "value": "OFF" } } } ] }
Дальше были определены типы сборки
{ "version": 6, "include": [ "base.json", "selftest.json", "license.json", "linux_x86.json" ], "configurePresets": [ { "name": "debug", "hidden": true, "inherits": ["linux"], "binaryDir": "${sourceDir}/build/Debug", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } }, { "name": "Debug", "displayName": "Debug", "inherits": ["disable-license", "disable-self-test", "base", "debug"] }, { "name": "release", "hidden": true, "inherits": ["base", "enable-self-test", "enable-license"], "binaryDir": "${sourceDir}/build/Release", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } } ], "buildPresets": [ { "name": "Debug", "configurePreset": "Debug" } ] }
Завершающим этапом выступает формирование вызываемых пресетов
{ "version": 6, "include": [ ".hidden/arm_linux.json", ".hidden/build_type.json", ".hidden/license.json", ".hidden/linux_x86.json", ".hidden/mips.json", ".hidden/windows.json" ], "configurePresets": [ { "name": "itvpn-release", "hidden": true, "inherits": ["vpn-license", "release"] }, { "name": "itvpn-release-linux-x86", "displayName": "itVPN Release Linux_x86", "inherits": ["linux", "itvpn-release"] }, { "name": "itvpn-release-win32", "displayName": "itVPN Release WIN32", "inherits": ["win32", "itvpn-release"] }, //... ], "buildPresets": [ { "name": "itvpn-release-linux-x86", "configurePreset": "itvpn-release-linux-x86" }, { "name": "itvpn-release-win32", "configurePreset": "itvpn-release-win32" }, //... ] }
По завершении работы над пресетами можно наблюдать следующую иерархию пресетов

Рассмотренный пример демонстрирует как можно использовать CMake пресеты в модульных библиотеках, которые помимо настроек сборки поддерживают возможность конфигурирования самой библиотеки.
ссылка на оригинал статьи https://habr.com/ru/articles/892466/
Добавить комментарий