
Здравствуйте, дорогие читатели Хабра! Я давно хотел поделиться своими знаниями о работе с реестрами под ключ, так как нигде нет четкой и последовательной информации по этой теме. Сегодня мы разберем, как управлять зависимостями через реестры 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


Т.к. я уже выполнял, у меня они прокэшировались на сервере и были восстановлены от туда. Кэширование завершено.
Сборка тестового проекта, с использованием 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
Спасибо за внимание!