Управление зависимостями на C++ с помощью vcpkg registry и сервера кеширования

от автора

Здравствуйте, дорогие читатели Хабра! Я давно хотел поделиться своими знаниями о работе с реестрами под ключ, так как нигде нет четкой и последовательной информации по этой теме. Сегодня мы разберем, как управлять зависимостями через реестры vcpkg и как кэшировать их на сервере. Статья рассчитана на пользователя, который имел опыт работы с vcpkg или conan

Ссылки на документацию

Содержание

  • Создание реестра

  • Создание своих пакетов Qt5/Boost

  • Сборка standalone окружения

  • Организация кэширования бинарей на сервере nuget

  • Сборка тестового проекта, с использованием vcpkg + nuget (кеширование)

Создание реестра

Создаем новый удаленный Git-репозиторий (подойдет любой, к которому у вас есть доступ). В статье я буду использовать свой репозиторий (ссылка)

Для первоначальной настройки потребуется файл baseline.json в папке versions.

versions/baseline.json
{   "default": {   } } 

Коммитим. Пушим. Пустой реестр создан. Для удобства работы можно взять скрипты для автоматизации из папки scripts.

Создание своих пакетов

Рекомендации по созданию port-файлов можно найти в официальной документации. В данной статье не предполагается подробное освещение этого вопроса.

В процессе работы мне потребуется использовать библиотеку Boost версии 1.87. В настоящее время в реестре доступна версия 1.88, однако её переопределение в vcpkg является для меня неудобным. Кроме того, я намерен использовать сборку qt5-base из исходников на зеркале Яндекса. Для достижения этой цели необходимо будет выполнить следующие действия:

  • Произвести форк файла port qt5-base.

  • Создать собственный файл port для библиотеки Boost.

Port qt5-base

За основе я взял актуальный port qt5-base. Копируем из vcpkg/ports в наш репозитори port/qt5-base

Структура репозитория

├───ports
│ ├───boost
│ └───qt5-base
│ ├───cmake
│ └───patches
├───scripts
├───share
└───versions

Редактируем источник скачивания (файл registry/ports/qt5-base/cmake/qt_download_submodule.cmake

    set(FULL_VERSION "${QT_MAJOR_MINOR_VER}.${QT_PATCH_VER}")     set(ARCHIVE_NAME "${NAME}-everywhere-opensource-src-${FULL_VERSION}.tar.xz")     set(URLS         "https://mirror.yandex.ru/mirrors/qt.io/${QT_MAJOR_MINOR_VER}/${FULL_VERSION}/submodules/${ARCHIVE_NAME}"     )

Порядок действий для обновления информации о пакете:

  • Коммитим файлы в папке ports

  • Форматируем порт-фалы (scripts/format-ports.py)

cd scripts py format-ports.py
  • Коммитим изменения, если есть.

  • Обновляем актуальную информацию о версиях и снова коммитим изменения

cd scripts py update-versions.py
пример вывода
Пример обновления версий

Пример обновления версий

После выполнения данных шагов у вас появятся автоматически сгенерированые папки и файлы в versions (не нужно их редактировать руками!)

Пример файла versions/q-/qt5-base.json
{   "versions": [     {       "git-tree": "e04eef6f4c169b57fd43a68a1a3d2bd9ef6d6ec2",       "version": "5.15.16",       "port-version": 4     }   ] } 

Этот файл содержит все версии и привязки к конкретным коммитам вашего port-файла.

Port boost

Все основные шаги по добавлению и обновлению описаны при добавлении qt5-base и заострять большое внимание на них не будем.

Расскажу кратко. Мой portfile будет

  • Собирать все библиотеки в рамках 1 порт-файла

  • Нужной мне версии

  • Представлены 2 фичи — принудительно static, принудительно shared

мой portfile.cmake
 string(REPLACE "." "-" ARCHIVE_VERSION ${VERSION})  set(BOOST_ARVHICE_FILENAME boost-${ARCHIVE_VERSION}.zip)  vcpkg_download_distfile(     ARCHIVE_ZIP     URLS https://github.com/boostorg/boost/releases/download/boost-${VERSION}/boost-${VERSION}-cmake.7z     FILENAME ${BOOST_ARVHICE_FILENAME}     SHA512 994356c84f4b96e263087eff40e53298791e7d72c6d24dd2cc3988c32edaa880180d39401504befe5c1b197ebead77c855452c608c6560a42f50f5a3016e0add )  vcpkg_extract_source_archive(     SOURCE_PATH     ARCHIVE "${ARCHIVE_ZIP}" )  string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "dynamic" BUILD_SHARED) string(COMPARE EQUAL "${VCPKG_CRT_LINKAGE}" "static" BUILD_STATIC_CRT)  if(BUILD_SHARED)     set(OPTION_BUILD_SHARED_LIBS ON) else()     set(OPTION_BUILD_SHARED_LIBS OFF) endif()  if(BUILD_STATIC_CRT)     set(BOOST_RUNTIME_LINK static) else()     set(BOOST_RUNTIME_LINK shared) endif()   if("force-static" IN_LIST FEATURES)     set(OPTION_BUILD_SHARED_LIBS OFF) #rewrite options     vcpkg_check_linkage(ONLY_STATIC_LIBRARY) endif()  if("force-shared" IN_LIST FEATURES)     set(OPTION_BUILD_SHARED_LIBS ON) #rewrite options     vcpkg_check_linkage(ONLY_DYNAMIC_LIBRARY) endif()   set(BOOST_EXCLUDE_COMPONENTS test log wave stacktrace python) list(JOIN BOOST_EXCLUDE_COMPONENTS "\\;" BOOST_EXCLUDE_LIBRARIES)  message(STATUS "Replace BoostConfig with fixed search lib_DIR") configure_file(${CMAKE_CURRENT_LIST_DIR}/BoostConfig.cmake.in ${SOURCE_PATH}/tools/cmake/config/BoostConfig.cmake USE_SOURCE_PERMISSIONS @ONLY)  # disable check LINUX_VERSION_CODE in ASTRA if(UNIX)         file(COPY_FILE ${CMAKE_CURRENT_LIST_DIR}/config.hpp ${SOURCE_PATH}/libs/asio/include/boost/asio/detail/config.hpp) endif()  message(STATUS "Building boost with BUILD_SHARED_LIBS=${OPTION_BUILD_SHARED_LIBS}, CRT=${BOOST_RUNTIME_LINK} EXCLUDE:${BOOST_EXCLUDE_COMPONENTS}")  vcpkg_cmake_configure(     SOURCE_PATH ${SOURCE_PATH}     WINDOWS_USE_MSBUILD     OPTIONS         -DBUILD_SHARED_LIBS=${OPTION_BUILD_SHARED_LIBS}         -DBOOST_RUNTIME_LINK=${BOOST_RUNTIME_LINK}         -DBOOST_EXCLUDE_LIBRARIES=${BOOST_EXCLUDE_LIBRARIES}         -DBOOST_ENABLE_PYTHON=OFF         -DBUILD_TESTING=OFF )  vcpkg_cmake_install()  vcpkg_cmake_config_fixup(PACKAGE_NAME boost CONFIG_PATH lib/cmake/Boost-${VERSION} DO_NOT_DELETE_PARENT_CONFIG_PATH) #main config  vcpkg_copy_pdbs()  # Fixup configs for debug and release function(_fixup_boost_config_for_dir_ target_dir)      message(STATUS "Fixup boost configs for dir: ${target_dir}")     file(GLOB CONFIG_LIST LIST_DIRECTORIES true ${target_dir}/boost_*)      foreach(dir ${CONFIG_LIST})         IF(IS_DIRECTORY ${dir})              string(REPLACE "${target_dir}/" "" dir_name "${dir}")             #string(REPLACE "-1.83.0" "" PKG_CONFIG_NAME ${dir_name})             string(REPLACE "-${VERSION}" "" PKG_CONFIG_NAME ${dir_name})             message(STATUS "try fixup: ${dir_name} - ${PKG_CONFIG_NAME}")              vcpkg_cmake_config_fixup(PACKAGE_NAME ${PKG_CONFIG_NAME} CONFIG_PATH lib/cmake/${dir_name} DO_NOT_DELETE_PARENT_CONFIG_PATH)         ELSE()             CONTINUE()         ENDIF()     endforeach()      unset(CONFIG_LIST) endfunction()  _fixup_boost_config_for_dir_(${CURRENT_PACKAGES_DIR}/debug/lib/cmake) _fixup_boost_config_for_dir_(${CURRENT_PACKAGES_DIR}/lib/cmake)  file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/lib/cmake")  file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/lib/cmake")   file(INSTALL "${SOURCE_PATH}/LICENSE_1_0.txt" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright) 

Все изменения можно посмотреть в репозитории

Повторяем все действия по обновлению версий / коммитов, как в qt5-base

Сборка standalone окружения

С 2024 года vcpkg научился делать standalone окружения. Окружение может быть экспортировано полностью самостоятельным, отвязанным от основного репозитория vcpkg.

Для создания окружения нам будет необходимо подготовить два файла:

  • vcpkg.json — файл манифеста зависимостей

  • vcpkg-configuration.json — файл описания реестров, откуда что брать.

Для упрощения работы был написан вспомогательный скрипт make-vcpkg-configuration.py. Данный скрипт проходит по текущему реестру и собирает все версии, формируя эталонный конфигурации с привязкой к комиту и репозиторию.

пример подготовки файлов
cd scripts py make-vcpkg-configuration.py

Будут сформированы два файла

Пример выполнения команды

Пример выполнения команды
vcpkg.json
{     "dependencies": [],     "overrides": [         {             "name": "boost",             "version": "1.87.0#2"         },         {             "name": "qt5-base",             "version": "5.15.16#4"         }     ] }
vcpkg-configuration.json
{     "default-registry": {         "kind": "git",         "repository": "https://github.com/microsoft/vcpkg.git",         "baseline": "3425f537d2f01c761d2d2cff59a7577eb4f568c0"     },     "registries": [         {             "kind": "git",             "repository": "https://github.com/SaulBerrenson/registry.git",             "reference": "main",             "packages": [                 "boost",                 "qt5-base"             ],             "baseline": "f3b8c64ae49a1dfd4cd845767e7d14f6f03bd09d"         }     ] }

Эти файлы можно заполнить руками, но скриптом проще и это универсальное решение.

Заполняем реестр теми пакетами, которые хотим видеть в будущем окружении

пример заполнения (vcpkg.json)
{     "dependencies": [ {             "name": "boost",             "features": ["force-static"]         } ],     "overrides": [         {             "name": "boost",             "version": "1.87.0#2"         },         {             "name": "qt5-base",             "version": "5.15.16#4"         }     ] }

Выбираем удобую папку для сборки окружения и переносим туда:

  • vcpkg.json

  • vcpkg-configuration.json

  • vcpkg_manager.py (вспомогательный скрипт для удобства сборки окружений)

py vcpkg_manager.py --cache local --triplet x64-windows --steps install export  PS C:\projects\article\build\env\v142_x64> Get-ChildItem -Recurse -Depth 1 | Select-Object @{Name="Level";Expression={($_.FullName.Split('\').Count - (Get-Location).Path.Split('\').Count)}}, Name, FullName | Format-Table  Level Name         FullName ----- ----         --------     1 installed    C:\projects\article\build\env\v142_x64\installed     1 scripts      C:\projects\article\build\env\v142_x64\scripts     1 .vcpkg-root  C:\projects\article\build\env\v142_x64\.vcpkg-root     1 vcpkg.exe    C:\projects\article\build\env\v142_x64\vcpkg.exe     2 vcpkg        C:\projects\article\build\env\v142_x64\installed\vcpkg     2 x64-windows  C:\projects\article\build\env\v142_x64\installed\x64-windows     2 buildsystems C:\projects\article\build\env\v142_x64\scripts\buildsystems     2 cmake        C:\projects\article\build\env\v142_x64\scripts\cmake

После выполнения мы получаем полностью отвязанное окружение от vcpkg. Для его использования необходимо передавать toolchainFile не из репозитория vcpkg, а из сформированной папки — тогда он не будет учитывать только это окружение.

Организация кэширования бинарей на сервере nuget

Сборка изолированных окружение заманчива, но иногда, особенно, в целях автоматизации CI\CD и т.д. — есть желание прокешировать пакеты аналогично conan.

Подготовка

Выбрать можно любой nuget-сервер, я выбираю бесплатный и простой open-source проект Baget. Разворачивать все будем в докере. Я заранее подготовил уже готовую конфигурацию на базе docker (ссылка). Авторизации никакой не будет — организация доступа не цель статьи.

docker-compose.yml

version: ‘3.8’

services:
baget:
image: loicsharma/baget
container_name: baget
ports:
— «5555:80»
volumes:
— ./baget-data:/var/baget
environment:
— ApiKey__IsRequired=false
— Storage__Type=FileSystem
— Storage__Path=/var/baget/packages
— Database__Type=PostgreSql
— Database__ConnectionString=Host=postgres;Port=5432;Database=baget;Username=baget;Password=baget_password
— Search__Type=Database
networks:
— baget-network
depends_on:
— postgres

postgres:
image: postgres:15-alpine
container_name: postgres
environment:
— POSTGRES_USER=baget
— POSTGRES_PASSWORD=baget_password
— POSTGRES_DB=baget
ports:
— «5432:5432»
volumes:
— postgres-data:/var/lib/postgresql/data
networks:
— baget-network

volumes:
postgres-data:

networks:
baget-network:
driver: bridge

docker-compose up -d

Важно: baget в стоке не умеет работать в postgresql с длинными наименованиями, который генерирует vcpkg. Фиксим это просто (сменой типов в ряде колонок)

docker exec -it postgres psql -U baget -d baget -c "ALTER TABLE \"Packages\" ALTER COLUMN \"Version\" TYPE TEXT; ALTER TABLE \"Packages\" ALTER COLUMN \"Id\" TYPE TEXT; ALTER TABLE \"Packages\" ALTER COLUMN \"OriginalVersion\" TYPE TEXT; ALTER TABLE \"PackageTypes\" ALTER COLUMN \"Version\" TYPE TEXT; ALTER TABLE \"Packages\" ALTER COLUMN \"Description\" TYPE TEXT;"

Сервер поднят — осталось наполнить пакетами.

Пустой сервер

Пустой сервер

Для организации кеширования можно воспользоваться vcpkg_manager.py

py vcpkg_manager.py --cache remote --triplet x64-windows --steps install export --nuget-url http://home-pc:5555/v3/index.json  # Указываем сервер куда заливать --nuget-url http://home-pc:5555/v3/index.json
Пример сборки окружения с учетом сервера кеширования

Пример сборки окружения с учетом сервера кеширования
Вид пакетов в nuge

Вид пакетов в nuge

Т.к. я уже выполнял, у меня они прокэшировались на сервере и были восстановлены от туда. Кэширование завершено.

Сборка тестового проекта, с использованием vcpkg + nuget (кеширование)

Итак, на сервере у нас уже имеются нужные нам пакеты. Для конфигурирования проекта cmake на понадобится использовать режим манифеста и наши файлы vcpkg.json и vcpkg-configuration.json, которые мы использовали для сборки зависимостей.

Примерная структура проекта

Примерная структура проекта cmake
Level Name ----- ----     1 CMakeLists.txt     1 CMakePresets.json     1 main.cpp     1 vcpkg-configuration.json     1 vcpkg.json

Для успешного конфигурирования нам понадобится:

  • toolchainFile — путь до vcpkg.cmake в основном репозитории (где у вас установлен vcpkg)

  • vcpkg.json

  • vcpkg-configuration.json

  • Переменная окружения VCPKG_BINARY_SOURCES с описанием источника пакетов:

"VCPKG_BINARY_SOURCES": "clear;nuget,http://home-pc:5555/v3/index.json,readwrite;nugettimeout,600"

Для удобства я использовал cmake presets

   "configurePresets": [      {        "name": "windows-base",        "hidden": true,        "generator": "Ninja",        "binaryDir": "${sourceDir}/out/build/${presetName}",        "installDir": "${sourceDir}/out/install/${presetName}",        "toolchainFile": "c:\\buildtools\\vcpkg\\scripts\\buildsystems\\vcpkg.cmake",        "environment": {          "VCPKG_BINARY_SOURCES": "clear;nuget,http://home-pc:5555/v3/index.json,readwrite;nugettimeout,600"        },        "cacheVariables": {          "CMAKE_C_COMPILER": "cl.exe",          "CMAKE_CXX_COMPILER": "cl.exe",          "VCPKG_TARGET_TRIPLET": "x64-windows"        },        "condition": {          "type": "equals",          "lhs": "${hostSystemName}",          "rhs": "Windows"        }      }
Пример вывода конфигурирования проекта
Вывод конфигурирование проекта

Вывод конфигурирование проекта

Итак, мы сегодня научились:

  • делать простейший реестр пакетов

  • собирать окружение standalone

  • собирать и кэшировать окружение на удаленном сервере, подобно conan

Спасибо за внимание!


ссылка на оригинал статьи https://habr.com/ru/articles/916620/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *