vcpkg для досвідчених: версії, features, реєстри, власні порти, кеш і діагностика
vcpkg для досвідчених: версії, features, реєстри, власні порти, кеш і діагностика
🎯 Мета лекції
- Зафіксувати версії залежностей так, щоб збірка відтворювалась через роки.
- Керувати необов'язковими можливостями пакетів (features) та умовними залежностями.
- Підключати власні порти й реєстри через
vcpkg-configuration.json. - Налаштувати бінарний кеш і використати vcpkg у CI.
- Діагностувати збої встановлення за логами.
🔑 Ключові терміни
- Baseline: коміт каталогу vcpkg, який визначає версії всіх портів за замовчуванням.
- Override: примусова версія конкретного пакета, яка перекриває baseline.
- Feature: необов'язкова можливість пакета, яка підключається окремо.
- Реєстр (registry): джерело портів: git-репозиторій або папка.
- Overlay-порт: порт, що підміняє або доповнює каталог, не змінюючи сам vcpkg.
Попередні лекції стосувались щоденної роботи з vcpkg. Цього вистачає, доки збірки робляться тут і зараз. Але програма живе роками, і через пів року vcpkg.json без додаткових налаштувань може встановити іншу версію бібліотеки. Нижче розглянуто, як це контролювати.
Версії залежностей
Проблема: каталог постійно змінюється
Каталог vcpkg — це git-репозиторій. Кожного тижня в ньому з'являються нові версії бібліотек. Якщо vcpkg.json перелічує лише назви ("fmt"), то яку версію отримає користувач, залежить від того, який коміт каталогу в нього зараз. У вас сьогодні fmt 12.1.0, а через пів року колега отримає іншу.
Baseline: знімок каталогу

Поле builtin-baseline у vcpkg.json містить хеш коміту каталогу. vcpkg бере версії портів саме з цього коміту, незалежно від поточного стану репозиторію:
{
"name": "awesome-app",
"version": "1.0.0",
"dependencies": ["fmt", "spdlog"],
"builtin-baseline": "1d611efb49276c65d69fb6d010c2aaaf039322b0"
}
Поточний коміт можна підставити автоматично:
vcpkg x-update-baseline --add-initial-baseline
Якщо builtin-baseline уже є, ця ж команда оновлює його до останнього коміту каталогу (аналог оновлення залежностей у npm). Тому правило таке: baseline змінюється лише свідомо, окремим комітом із перевіркою збірки.
builtin-baseline vcpkg не вміє працювати з обмеженнями версій: поля version>= та overrides потребують baseline. Формат значення: 40-символьний хеш коміту, а не дата й не назва гілки.Мінімальна версія: version>=
Об'єкт залежності з полем version>= задає нижню межу:
{
"name": "awesome-app",
"version": "1.0.0",
"dependencies": [
{ "name": "fmt", "version>=": "10.0.0" },
"spdlog"
],
"builtin-baseline": "1d611efb49276c65d69fb6d010c2aaaf039322b0"
}
Запис означає «не нижче 10.0.0». Конкретну версію вибирає baseline: якщо в ньому fmt 12.1.0, буде 12.1.0. Якщо baseline старіший за вимогу, vcpkg повідомить про помилку. Ця межа зручна для бібліотек, які вимагають мінімальної версії залежності, але не фіксують її жорстко.
Точна версія: overrides
Блок overrides примусово вибирає версію пакета, ігноруючи baseline:
{
"name": "version-demo",
"version": "1.0.0",
"dependencies": [
{ "name": "fmt", "version>=": "10.0.0" }
],
"overrides": [
{ "name": "fmt", "version": "10.2.1" }
],
"builtin-baseline": "1d611efb49276c65d69fb6d010c2aaaf039322b0"
}
Каталог на цьому комміті містить fmt 12.1.0, але проєкт просить 10.2.1. Результат реального запуску:
Програма, яка друкує FMT_VERSION, підтвердила: зібрана саме 10.2.1. Звідки vcpkg взяв стару версію, якщо каталог її не містить? З бази версій (папка versions/ у репозиторії каталогу): вона зберігає записи про всі версії порту, які колись були опубліковані, разом із комітами, де лежав порт тієї версії.
| Механізм | Що робить | Типове застосування |
|---|---|---|
builtin-baseline | Задає знімок каталогу для всіх портів | Застосунки: фіксація середовища |
version>= | Нижня межа версії | Бібліотеки: вимога до залежності |
overrides | Примусова версія одного пакета | Застосунки: відкат або заморожування конкретного пакета |
x-update-baseline. overrides потрібні, коли окрему бібліотеку слід тримати на старій версії (наприклад, через зламану сумісність в новій) на тлі свіжих інших пакетів.Features: необов'язкові можливості пакетів
Багато бібліотек мають частини, які потрібні не всім: підтримка HTTPS, додаткові формати, бекенди. Для цього порт оголошує features. Їх було видно у виводі vcpkg search як рядки порт[feature]:
cpp-httplib 0.27.0 A single file C++11 header-only HTTP/HTTPS server and client library
cpp-httplib[brotli] Enables brotli compression support using brotli
cpp-httplib[openssl] Enables HTTPS support using OpenSSL
spdlog[wchar] Build with wchar_t (Windows only)
Features у manifest
{
"name": "web-app",
"version": "1.0.0",
"dependencies": [
"fmt",
{
"name": "cpp-httplib",
"features": ["openssl"]
},
{
"name": "spdlog",
"default-features": false
}
],
"builtin-baseline": "1d611efb49276c65d69fb6d010c2aaaf039322b0"
}
"features": ["openssl"]додає до пакетаcpp-httplibпідтримку HTTPS: разом із ним буде встановлено й залежність OpenSSL."default-features": falseвимикає ті можливості, які порт вмикає за замовчуванням. Так збирається мінімальна версія: менше залежностей і швидша збірка.
Features у класичному режимі
Через командний рядок features записуються в квадратних дужках. Оболонка (особливо zsh) намагається розкрити їх як шаблон, тому запис беруть у лапки:
vcpkg install "cpp-httplib[openssl]"
boost-filesystem, boost-asio, boost-beast та інші, а не на features одного порту boost. Тому команда зі старих прикладів vcpkg install boost[filesystem,system,thread] не відповідає реальній структурі. Правильно: vcpkg install boost-filesystem boost-system boost-thread, а у manifest додати кожен boost-* пакет окремо.Умовні залежності
Залежність можна обмежити платформою за допомогою поля platform:
{
"dependencies": [
"fmt",
{ "name": "pthreads", "platform": "windows" },
{ "name": "libuv", "platform": "!windows" }
]
}
Вираз у platform складається з імен платформ (windows, linux, osx, android, x64, arm64 та інших), логічних &, | і !. Пакет, для якого вираз хибний, пропускається. Поле "host": true позначає залежність, потрібну під час збірки на комп'ютері розробника (інструменти на кшталт protoc), а не в кінцевому застосунку.
Features самого проєкту
Власні групи залежностей проєкту (наприклад, для тестів) описуються в корені vcpkg.json:
{
"name": "awesome-app",
"version": "1.0.0",
"dependencies": ["fmt"],
"features": {
"tests": {
"description": "Залежності для тестів",
"dependencies": ["gtest"]
}
},
"builtin-baseline": "1d611efb49276c65d69fb6d010c2aaaf039322b0"
}
Увімкнути групу можна змінною CMake:
cmake -B build -DVCPKG_MANIFEST_FEATURES=tests \
-DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
Тепер gtest потрібен лише тим, хто збирає з -DVCPKG_MANIFEST_FEATURES=tests, а звичайний користувач завантажувати тестовий фреймворк не буде.
vcpkg-configuration.json
Окрім vcpkg.json, у проєкті може бути другий файл, vcpkg-configuration.json. Він описує звідки vcpkg бере порти. Створити обидва файли можна командою vcpkg new:
Файл vcpkg.json створюється порожнім: далі його заповнюють командою vcpkg add port <ім'я>. Поле default-registry вказує основний каталог і baseline: це альтернативний запис того самого builtin-baseline.
Реєстри
Реєстр — це джерело портів. Основний реєстр — публічний каталог Microsoft. Додатковими можуть бути ваші власні: наприклад, корпоративні бібліотеки, які не виставляються назовні.
{
"default-registry": {
"kind": "git",
"repository": "https://github.com/microsoft/vcpkg",
"baseline": "1d611efb49276c65d69fb6d010c2aaaf039322b0"
},
"registries": [
{
"kind": "git",
"repository": "https://github.com/my-org/vcpkg-registry",
"baseline": "7f4b0e2c5a1d6e9a8b3c4d5e6f708192a3b4c5d6",
"packages": ["my-core", "my-network"]
}
]
}
Поле packages перелічує, які порти беруться з цього реєстру. Усе інше надходить із основного. Реєстри бувають типів git (репозиторій) і filesystem (папка на диску або мережевий ресурс). Значення baseline у прикладі умовне: підставте хеш реального коміту вашого реєстру.
Overlay-порти та overlay-triplets
Простіший спосіб додати власну бібліотеку без реєстру — overlay: папка з портами, що підключається до каталогу:
{
"overlay-ports": ["./overlay-ports"],
"overlay-triplets": ["./triplets"]
}
Порти з overlay мають вищий пріоритет, ніж каталог. Тому overlay підходить і для нових бібліотек, і для підміни існуючого порту виправленою копією, поки зміни не прийняли в основний каталог. Шляхи рахуються відносно файлу vcpkg-configuration.json.
Власний порт
Порт складається з трьох основних файлів: vcpkg.json (метадані), portfile.cmake (інструкція збірки) та, за бажанням, usage (підказка користувачу). Нижче порт mylib створено повністю локально, як overlay: код бібліотеки лежить поруч із портом, тож завантажувати нічого не треба.
Розбір portfile.cmake
Усі команди vcpkg_* — функції, які vcpkg надає портам.
vcpkg_cmake_configure(SOURCE_PATH ...)конфігурує CMake-проєкт бібліотеки для Debug і Release (vcpkg збирає обидва варіанти).vcpkg_cmake_install()збирає і встановлює бібліотеку у тимчасову папку пакета.vcpkg_cmake_config_fixup(CONFIG_PATH ...)виправляє шляхи у згенерованих Config-файлах, щоб вони були відносними й працювали з папкоюinstalled.file(REMOVE_RECURSE ".../debug/include")— заголовки вdebug/є дублікатом, а vcpkg перевіряє пакет на такі зайві файли.vcpkg_install_copyrightкопіює ліцензію: пакет без файлуcopyrightvcpkg не прийме.
Запуск показує, що vcpkg знаходить overlay-порт і збирає його так само, як порти каталогу:
Рядок info: installing overlay port from here підтверджує, що порт узято з overlay, а не з каталогу.
usage в portfile.cmake vcpkg виводить попередження: «this port contains a usage file but doesn't install it», і підказує готовий рядок file(INSTALL ...). Рядок, який використано в portfile.cmake вище, узято саме з цієї підказки. Читайте повідомлення vcpkg: вони точні.Порт для зовнішньої бібліотеки
Коли код бібліотеки лежить у GitHub, порт завантажує його замість локальної папки:
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO user/mylib
REF v1.0.0
SHA512 <хеш-архіву>
HEAD_REF main
)
vcpkg_cmake_configure(SOURCE_PATH "${SOURCE_PATH}")
vcpkg_cmake_install()
vcpkg_cmake_config_fixup(PACKAGE_NAME mylib)
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE")
Поле SHA512 — контрольна сума архіву, яка гарантує, що завантажений код не змінився. Значення не вигадують: завантажте архів і обчисліть хеш командою vcpkg hash <файл>. Другий спосіб: тимчасово вказати SHA512 0, і vcpkg під час збірки виведе очікуване значення в повідомленні про помилку.
vcpkg create mylib https://github.com/user/mylib/archive/v1.0.0.tar.gz mylib-1.0.0.tar.gz. Вона розміщує файли в папці ports/mylib самого каталогу vcpkg. Для проєктних портів зручніше скопіювати їх в overlay-ports/.Бінарний кеш
Як він працює
Для кожного пакета vcpkg обчислює ключ ABI (хеш) з усіх вхідних даних: версії порту, його файлів, компілятора, triplet, версій залежностей. Якщо в кеші є архів із таким ключем, його розпаковують замість збірки. Хеш змінюється при будь-якій зміні вхідних даних, тому застарілий пакет використаний не буде.
Кеш за замовчуванням лежить у домашній папці користувача: ~/.cache/vcpkg/archives на Linux та macOS і %LOCALAPPDATA%\vcpkg\archives на Windows. Усередині архіви розкладені по підпапках за двосимвольним префіксом хешу: наприклад, пакет із ключем 2a9f... лежить у підпапці 2a. Тому в реальному кеші видно набір папок 2a, 7d, 9f і так далі, а в кожній по кілька файлів .zip.
У попередніх лекціях цей кеш уже працював: повторне встановлення fmt займало 12 мс, бо vcpkg писав Restored 3 package(s) from .../archives.
Налаштування
# Інша папка для кешу
export VCPKG_DEFAULT_BINARY_CACHE=$HOME/.vcpkg-cache
mkdir -p $VCPKG_DEFAULT_BINARY_CACHE
# Повний контроль через VCPKG_BINARY_SOURCES (джерела розділені крапкою з комою)
export VCPKG_BINARY_SOURCES="clear;files,$HOME/.vcpkg-cache,readwrite"
Значення VCPKG_BINARY_SOURCES складається зі списку джерел, розділених ;. Слово clear скидає джерела за замовчуванням, а files,<папка>,readwrite додає папку для читання й запису. Інші джерела: nuget (канал NuGet, поширений для CI), http (власний сервер), x-azblob (Azure Blob Storage), x-aws (Amazon S3).
vcpkg install fmt --binarysource=clear;files,C:\vcpkg-cache,readwriteбез лапок. В оболонці bash символ ; розділяє команди, тож усе після першої крапки з комою виконується як окрема команда. Користуйтеся змінною середовища або беріть значення параметра в лапки: --binarysource="clear;files,/шлях,readwrite".vcpkg у CI
Автоматичні збірки на чистій машині за замовчуванням починають з порожнього кешу, тому кожен запуск збирає все з нуля. Рішення: зберігати папку кешу між запусками. Приклад для GitHub Actions:
name: build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
env:
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/.vcpkg-cache
steps:
- uses: actions/checkout@v4
- name: Кеш бінарних пакетів vcpkg
uses: actions/cache@v4
with:
path: ${{ env.VCPKG_DEFAULT_BINARY_CACHE }}
key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json', 'vcpkg-configuration.json') }}
- name: Підготовка папки кешу
run: mkdir -p "$VCPKG_DEFAULT_BINARY_CACHE"
- name: Конфігурація
run: >
cmake -B build -S .
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake
- name: Збірка
run: cmake --build build --parallel
- name: Тести
run: ctest --test-dir build --output-on-failure
Що тут важливо:
VCPKG_INSTALLATION_ROOT— змінна, яку GitHub встановлює на своїх образах із vcpkg. Встановлювати vcpkg вручну не потрібно.- Ключ кешу залежить від хешу
vcpkg.json. Поки залежності не змінились, кеш використовується. Додали бібліотеку: хеш інший, ключ інший, і кеш збудується заново. mkdir -pпотрібен, бо vcpkg очікує, що папка існує.
Діагностика проблем
Де шукати причину
Якщо порт не зібрався, vcpkg виводить шлях до логів. Вони лежать у buildtrees/<порт>/:
| Файл | Зміст |
|---|---|
config-<triplet>-out.log | Звичайний вивід конфігурації CMake |
config-<triplet>-err.log | Повідомлення про помилки конфігурації |
config-<triplet>-dbg-CMakeCache.txt.log | Кеш CMake для Debug-збірки |
<triplet>.vcpkg_abi_info.txt | Дані, з яких обчислено ключ ABI |
install-<triplet>-*.log, build-*.log | Вивід компіляції та установки |
Першим дивитесь файл *-err.log: найчастіше причина записана там у кількох останніх рядках.
Типові проблеми
Restored N package(s) у виводі), і використовуйте triplet, що збирає тільки Release (x64-windows-release, arm64-osx-release), якщо Debug вам не потрібен. У CI додайте кеш папки з архівами.CMAKE_TOOLCHAIN_FILE при першій конфігурації папки. Чи вказано CONFIG у find_package. Чи збігається ім'я пакета в find_package з тим, що підказує файл usage порту. Потім видаліть build/ і сконфігуруйте заново.git pull у папці vcpkg (або git fetch --unshallow, якщо клон був неповним) і перевірте, що в builtin-baseline стоїть повний 40-символьний хеш, а не скорочений.pkg-config, autoconf, nasm, perl. У логу буде повідомлення на кшталт «Could not find pkg-config». Встановіть інструмент системним менеджером (apt install pkg-config, brew install pkg-config) і повторіть.vcpkg remove --recurse <пакет>, потім vcpkg install <пакет>. У manifest-режимі достатньо видалити папку build/vcpkg_installed. Якщо проблема в застарілому кеші, тимчасово вимкніть його: --binarysource=clear. Папки buildtrees/ та packages/ у каталозі vcpkg можна видаляти повністю: це лише тимчасові файли збірки.Практичне завдання
- Створіть проєкт із залежностями
fmtіspdlogта додайтеbuiltin-baseline. - Зафіксуйте
fmtна версії 10.2.1 черезoverrides. ВиведітьFMT_VERSIONі переконайтесь, що збирається саме вона. - Додайте feature
testsз залежністюgtestта перевірте, що-DVCPKG_MANIFEST_FEATURES=testsвстановлює її, а звичайна конфігурація ні. - Створіть overlay-порт для власної невеликої бібліотеки на основі прикладу
mylib. - Налаштуйте окремий кеш у папці
.vcpkg-cacheі переконайтесь за рядкомRestored N package(s), що повторна конфігурація використовує його.
fmt:...@10.2.1; після -DVCPKG_MANIFEST_FEATURES=tests у списку пакетів з'являється gtest; для власної бібліотеки виводиться installing overlay port from here; при повторній конфігурації всі пакети відновлені з кешу.Резюме
📌 Версії
builtin-baseline фіксує знімок каталогу (40-символьний хеш), version>= задає нижню межу, overrides примусово вибирає версію.🎛️ Features
features, default-features), умовні залежності (platform, host) та власні групи проєкту через VCPKG_MANIFEST_FEATURES.🗃️ Реєстри і overlay
vcpkg-configuration.json підключає власні реєстри (git, filesystem), overlay-ports та overlay-triplets.🏗️ Власний порт
vcpkg.json плюс portfile.cmake з функціями vcpkg_cmake_*. Хеш завантажуваного архіву дає vcpkg hash.⚡ Кеш і CI
vcpkg.json.vcpkg і CMake: toolchain-файл, manifest vcpkg.json, IDE
Як підключити vcpkg до CMake-проєкту: toolchain-файл і порядок його підключення, змінні середовища, пресети, manifest-режим з vcpkg.json, повний приклад з fmt, spdlog, nlohmann-json і cxxopts, налаштування VS Code, Visual Studio та CLion.
Бібліотеки C++: fmt, spdlog і nlohmann/json
Форматування рядків бібліотекою fmt, логування через spdlog (рівні, sinks, ротація, шаблони), робота з JSON у nlohmann/json: створення, читання, файли, власні типи, обробка помилок. Підключення через vcpkg та CMake, перевірені приклади.