[2026] C++ vcpkg Basics: Complete Guide | Install, Manifest, Triplet, Versioning, Custom Ports [#53-3]
이 글의 핵심
Hands-on C++ vcpkg basics: install, Manifest mode, triplets, versioning, and custom overlay ports. Covers find_package(fmt) failures, CI-only breaks, teammate drift, static vs dynamic linking, and integrating internal libraries.
Introduction: “Why is vcpkg so complicated?”
Problem scenarios
"find_package(fmt) failed — CMake cannot find fmt"
"It builds locally but CI fails"
"Teammate A builds fine; B fails — different vcpkg versions"
"Boost takes 30+ minutes to install"
"I need static linking for deployment but only get dynamic"
"I need a library that is not in the official registry"
"undefined reference but headers resolve fine"
"I ran vcpkg install but CMake still cannot find the package"
This article is a complete vcpkg basics guide. From installation through Manifest mode, triplets, versioning, and custom ports, it walks through practical examples. It covers real-world scenarios, frequent errors, best practices, and production patterns in depth.
After reading you will be able to:
- Install vcpkg and hook it into a CMake project.
- Manage per-project dependencies with Manifest mode.
- Choose dynamic/static linking and platforms with triplets.
- Pin reproducible builds with builtin-baseline and vcpkg.lock.
- Integrate internal libraries via custom ports (overlays).
- Recognize common errors and how to fix them.
Requirements: vcpkg 2024.01+, CMake 3.20+, C++17 or later
Production experience: This post is grounded in real issues and fixes from large C++ codebases. It includes practical pitfalls and debugging tips that textbooks often skip.
Table of contents
- Problem scenarios in detail
- Installing vcpkg
- Manifest mode walkthrough
- Triplet basics
- Versioning
- Custom ports (overlay)
- End-to-end example projects
- Common errors and fixes
- Best practices
- Production patterns
- Implementation checklist
1. Problem scenarios in detail
Scenario 1: New project — dependency installation hell
Situation: You want to add fmt and spdlog to a C++ project
Problem: Repeated manual download, build, and path wiring
Outcome: Declare dependencies in vcpkg.json under Manifest mode → one coherent flow
Scenario 2: CI-only build failures
Situation: Local build succeeds; GitHub Actions fails find_package
Problem: Packages installed globally in Classic mode locally are missing in CI
Outcome: Manifest mode + vcpkg.json + CMAKE_TOOLCHAIN_FILE
Scenario 3: Everyone’s vcpkg state differs
Situation: Teammate A has fmt 10.1, B has 10.2
Problem: API differences — A succeeds, B fails
Outcome: Pin builtin-baseline and commit vcpkg.lock
Scenario 4: Static linking for shipping
Situation: You must ship a single binary (no DLLs)
Problem: Default triplets often prefer dynamic linking
Outcome: Use x64-windows-static (or the matching static triplet)
Scenario 5: Internal library integration
Situation: Internal utility library not in the public registry
Problem: vcpkg install cannot fetch it
Outcome: Register an overlay ports directory
Choosing features by scenario
| Scenario | vcpkg feature | How to apply |
|---|---|---|
| New project | Manifest mode | Add vcpkg.json |
| CI builds | Manifest + toolchain | CMAKE_TOOLCHAIN_FILE |
| Version alignment | builtin-baseline, vcpkg.lock | Commit vcpkg.json + lock |
| Static linking | Triplet | VCPKG_TARGET_TRIPLET |
| Internal libraries | Overlay | VCPKG_OVERLAY_PORTS |
flowchart TD
subgraph Problem[Practical problems]
P1[Dependency install hell] --> S1[Manifest mode]
P2[CI build failures] --> S2[Toolchain + vcpkg.json]
P3[Version skew] --> S3[baseline + lock]
P4[Static linking] --> S4[Triplet]
P5[Internal libraries] --> S5[Overlay]
end
2. Installing vcpkg
2.1 How to install
vcpkg clones the Git repository, then runs the bootstrap script to produce the executable.
Linux/macOS:
# 1. Clone vcpkg (outside the project or as a submodule)
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
# 2. Run bootstrap
./bootstrap-vcpkg.sh
# 3. Verify
./vcpkg version
Windows (PowerShell or cmd):
# 1. Clone vcpkg
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
# 2. Run bootstrap
.\bootstrap-vcpkg.bat
# 3. Verify
.\vcpkg.exe version
Result: You get vcpkg (or vcpkg.exe). Run it from the vcpkg directory or add it to PATH for global use.
2.2 Add vcpkg as a Git submodule (recommended)
To keep the same vcpkg revision across the team, add it as a submodule.
# From the project root
git submodule add https://github.com/Microsoft/vcpkg.git vcpkg
git submodule update --init --recursive
# Bootstrap vcpkg
cd vcpkg
./bootstrap-vcpkg.sh # Linux/macOS
# .\bootstrap-vcpkg.bat # Windows
cd ..
Benefit: vcpkg moves with the project revision, reducing “works on A’s machine, not B’s.”
2.3 Prerequisites
| Item | Minimum |
|---|---|
| Git | 2.x |
| CMake | 3.20+ |
| C++ compiler | MSVC 2019+, GCC 9+, Clang 10+ |
| Python | 3.8+ (some ports need it) |
cmake --version
g++ --version # Linux
clang++ --version # macOS
cl # Windows (Visual Studio Developer Command Prompt)
3. Manifest mode walkthrough
3.1 Classic mode vs Manifest mode
| Classic mode | Manifest mode | |
|---|---|---|
| Install style | vcpkg install fmt globally | Declare in vcpkg.json; install on configure |
| Where deps live | Under the vcpkg root | Per-project isolation |
| Reproducibility | Lower (differs per machine) | Higher (vcpkg.json + lock) |
| Recommendation | Legacy | Prefer Manifest for new work |
3.2 Minimal Manifest example
Layout:
my-app/
├── CMakeLists.txt
├── vcpkg.json
├── vcpkg/ # optional git submodule
└── src/
└── main.cpp
vcpkg.json:
// Example
{
"name": "my-app",
"version": "1.0.0",
"description": "vcpkg Manifest mode example",
"dependencies": [
"fmt",
"spdlog"
]
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.20)
project(my-app VERSION 1.0.0 LANGUAGES CXX)
# vcpkg toolchain (required!)
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake"
CACHE STRING "Vcpkg toolchain")
add_executable(my-app src/main.cpp)
find_package(fmt CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)
target_link_libraries(my-app PRIVATE
fmt::fmt
spdlog::spdlog
)
target_compile_features(my-app PRIVATE cxx_std_17)
src/main.cpp:
#include <spdlog/spdlog.h>
#include <fmt/core.h>
int main() {
spdlog::set_level(spdlog::level::debug);
spdlog::info("Hello, vcpkg! {}", fmt::format("vcpkg works!"));
return 0;
}
3.3 Build commands
# With vcpkg as a submodule
cmake -B build -S . \
-DCMAKE_TOOLCHAIN_FILE="$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake"
cmake --build build
./build/my-app # or build\my-app.exe on Windows
How vcpkg downloads and builds packages when you run CMake:
cmake -DCMAKE_TOOLCHAIN_FILE=.../vcpkg.cmake:
1. CMake loads the toolchain file:
Inside vcpkg.cmake:
- Sets VCPKG_INSTALLED_DIR
- Parses vcpkg.json
- Extracts the dependency list
2. Parse vcpkg.json:
{
"dependencies": ["fmt", "spdlog"]
}
→ Build dependency graph:
app
├─ fmt
└─ spdlog
3. Port metadata lookup:
For each package:
a. Search the registry for the port:
vcpkg/ports/fmt/vcpkg.json
vcpkg/ports/spdlog/vcpkg.json
b. Read version info:
vcpkg/versions/f-/fmt.json
→ "version": "10.1.1"
→ "git-tree": "abc123def456..."
c. Download port files:
git clone (sparse checkout)
OR read git tree object
4. Transitive dependency resolution:
spdlog/vcpkg.json:
{
"dependencies": ["fmt"]
}
Final graph:
app
├─ fmt (direct)
└─ spdlog
└─ fmt (transitive) → deduped
5. Compute Package ID (hash):
Package ID = hash(
package name,
version,
triplet,
port contents,
dependency hashes
)
e.g. fmt/10.1.1/x64-linux
→ package_id: 1a2b3c4d5e6f...
6. Cache lookup:
~/.cache/vcpkg/archives/ (Linux/Mac)
%LOCALAPPDATA%\vcpkg\archives\ (Windows)
if (package_id exists in cache):
→ extract only (fast!)
else:
→ download sources + build
7. Source download:
Read portfile.cmake:
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO fmtlib/fmt
REF 10.1.1
SHA512 abc123...
)
Steps:
a. Download tarball from GitHub:
https://github.com/fmtlib/fmt/archive/10.1.1.tar.gz
b. SHA512 verification:
downloaded_sha512 == expected_sha512?
→ error on mismatch
c. Extract:
downloads/fmt-10.1.1/
→ buildtrees/fmt/src/10.1.1-xxx/
8. Build:
portfile.cmake:
vcpkg_cmake_configure(
SOURCE_PATH ${SOURCE_PATH}
OPTIONS
-DFMT_DOC=OFF
-DFMT_TEST=OFF
)
vcpkg_cmake_build()
vcpkg_cmake_install()
Internally:
a. Configure CMake:
cd buildtrees/fmt/x64-linux-rel
cmake ../../src/ \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=packages/fmt_xxx
b. Build:
cmake --build . --target install
c. Install layout:
packages/fmt_xxx/
├── include/
│ └── fmt/
├── lib/
│ └── libfmt.a
└── share/
└── fmt/
└── fmtConfig.cmake
9. Package archive + cache:
packages/fmt_xxx/
→ zip: archives/1a2b3c4d5e6f.zip
→ stored for reuse
10. Copy into installed prefix:
packages/fmt_xxx/
→ installed/x64-linux/
├── include/fmt/
├── lib/libfmt.a
└── share/fmt/fmtConfig.cmake
11. find_package:
find_package(fmt CONFIG REQUIRED)
CMake search path:
- installed/x64-linux/share/fmt/
→ finds fmtConfig.cmake!
fmtConfig.cmake loads:
- defines target fmt::fmt
- headers: installed/x64-linux/include
- library: installed/x64-linux/lib/libfmt.a
12. target_link_libraries:
target_link_libraries(my-app PRIVATE fmt::fmt)
→ CMake uses fmt::fmt properties:
- INTERFACE_INCLUDE_DIRECTORIES
- INTERFACE_LINK_LIBRARIES
→ propagated to my-app
Directory layout under vcpkg_root/:
vcpkg_root/
├── downloads/ # source tarballs
│ └── fmt-10.1.1.tar.gz
├── buildtrees/ # intermediate build outputs
│ └── fmt/
│ └── src/
│ └── x64-linux-rel/
├── packages/ # built, pre-install
│ └── fmt_xxx/
├── installed/ # final prefix
│ └── x64-linux/
│ ├── include/
│ ├── lib/
│ └── share/
└── .cache/ (archives/) # binary cache
Per-triplet builds:
x64-windows (dynamic):
→ installed/x64-windows/bin/fmt.dll
→ installed/x64-windows/lib/fmt.lib
x64-windows-static:
→ installed/x64-windows-static/lib/fmt.lib (static)
Parallel builds:
fmt and nlohmann_json are independent:
→ can build in parallel
spdlog depends on fmt:
→ build spdlog after fmt
vcpkg orders dependencies automatically
Flow summary:
- CMake loads
CMAKE_TOOLCHAIN_FILE. - The vcpkg toolchain reads vcpkg.json.
- Missing packages are built automatically.
- find_package resolves against the installed prefix.
- Your target links and builds.
3.4 Dependency objects in Manifest mode
Use object form when you need version constraints or platform-specific dependencies.
{
"name": "my-app",
"version": "1.0.0",
"dependencies": [
"fmt",
{
"name": "spdlog",
"version>=": "1.11.0",
"version<": "2.0.0"
},
{
"name": "openssl",
"platform": "windows"
}
]
}
| Field | Meaning |
|---|---|
name | Package name |
version>= | Minimum version |
version< | Exclusive upper bound (avoids surprise breaks on major bumps) |
platform | Condition (windows, !windows, linux, osx) |
4. Triplet basics
4.1 What is a triplet?
A triplet follows an arch-vendor-os style name and selects architecture, OS, and linkage characteristics.
| Triplet | Description |
|---|---|
x64-windows | Windows 64-bit, dynamic linking |
x64-windows-static | Windows 64-bit, static linking |
x64-linux | Linux 64-bit |
x64-osx | macOS Intel |
arm64-osx | macOS Apple Silicon (M1/M2) |
4.2 How to set the triplet
Option 1: CMake cache variable
cmake -B build -S . \
-DCMAKE_TOOLCHAIN_FILE="$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake" \
-DVCPKG_TARGET_TRIPLET=x64-windows-static
Option 2: Environment variable
export VCPKG_DEFAULT_TRIPLET=x64-windows-static
cmake -B build -S .
Option 3: vcpkg-configuration.json
{
"default-registry": {
"kind": "git",
"repository": "https://github.com/microsoft/vcpkg",
"baseline": "..."
},
"triplet": "x64-windows-static"
}
4.3 Static vs dynamic
| Goal | Triplet |
|---|---|
| Development (faster iteration) | x64-windows, x64-linux |
| Shipping (single binary) | x64-windows-static, x64-linux-static |
4.4 Inspecting triplets
vcpkg version
vcpkg list
5. Versioning
5.1 builtin-baseline
builtin-baseline is a commit hash of the vcpkg ports registry. Available package versions depend on that revision.
{
"name": "my-app",
"version": "1.0.0",
"dependencies": ["fmt", "spdlog"],
"builtin-baseline": "a1b2c3d4e5f6789012345678901234567890abc"
}
Getting a baseline hash:
cd vcpkg
git pull
git rev-parse HEAD
# a1b2c3d4e5f6789012345678901234567890abc
Recommendation: Pin baseline in production; test upgrades on a branch before merging.
5.2 vcpkg.lock — reproducible builds
In Manifest mode, CMake integration can produce vcpkg.lock. Commit it to Git for bit-for-bit version alignment across machines.
{
"version": 1,
"port-version": 0,
"builtin-baseline": "a1b2c3d4e5f6789012345678901234567890abc",
"packages": [
{
"name": "fmt",
"version": "10.1.1",
"port-version": 0
},
{
"name": "spdlog",
"version": "1.12.1",
"port-version": 0
}
]
}
Note: When present, vcpkg prefers the lock. After dependency changes, the lock updates—retest and commit together.
5.3 Version constraint syntax
{
"dependencies": [
{
"name": "spdlog",
"version>=": "1.11.0",
"version<": "2.0.0"
}
]
}
version>=: minimum versionversion<: exclusive upper bound (guards against unexpected minor/major breaks)
6. Custom ports (overlay)
6.1 What is an overlay?
An overlay is a port directory tree that takes precedence over the official registry. Use it for internal libraries, patched ports, or packages not yet upstream.
6.2 Overlay directory layout
my-ports/
├── internal-lib/
│ ├── vcpkg.json
│ └── portfile.cmake
└── patched-spdlog/
├── vcpkg.json
├── portfile.cmake
└── patches/
└── fix-logging.patch
6.3 Minimal header-only overlay example
my-ports/header-only-lib/vcpkg.json:
{
"name": "header-only-lib",
"version": "1.0.0",
"description": "Header-only utility library",
"license": "MIT",
"dependencies": []
}
my-ports/header-only-lib/portfile.cmake:
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO example/header-only-lib
REF "v1.0.0"
SHA512 0
HEAD_REF main
)
# Copy headers only (no build)
file(INSTALL "${SOURCE_PATH}/include/"
DESTINATION "${CURRENT_PACKAGES_DIR}/include"
FILES_MATCHING PATTERN "*.hpp"
)
file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage"
DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE")
Tip: Leave SHA512 at 0, run install once—the error prints the real hash—paste it back.
6.4 Using an overlay
cmake -B build -S . \
-DCMAKE_TOOLCHAIN_FILE="$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake" \
-DVCPKG_OVERLAY_PORTS="$(pwd)/my-ports"
Add overlay ports as dependencies in vcpkg.json:
{
"dependencies": [
"fmt",
"header-only-lib"
]
}
6.5 Register overlays in vcpkg-configuration.json
Place vcpkg-configuration.json at the project root to apply overlays without extra CMake flags.
{
"default-registry": {
"kind": "git",
"repository": "https://github.com/microsoft/vcpkg",
"baseline": "a1b2c3d4e5f6..."
},
"overlay-ports": [./my-ports]
}
7. End-to-end example projects
7.1 Example 1: Basic Manifest mode
vcpkg-basics-demo/
├── CMakeLists.txt
├── vcpkg.json
├── vcpkg.lock # recommended to commit
├── vcpkg/ # git submodule
└── src/
└── main.cpp
vcpkg.json:
{
"name": "vcpkg-basics-demo",
"version": "1.0.0",
"description": "vcpkg basics demo",
"dependencies": [
"fmt",
"spdlog",
"nlohmann-json"
],
"builtin-baseline": "a1b2c3d4e5f6789012345678901234567890abc"
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.20)
project(vcpkg-basics-demo VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake"
CACHE STRING "Vcpkg toolchain")
add_executable(demo src/main.cpp)
find_package(fmt CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)
target_link_libraries(demo PRIVATE
fmt::fmt
spdlog::spdlog
nlohmann_json::nlohmann_json
)
target_compile_features(demo PRIVATE cxx_std_17)
src/main.cpp:
#include <spdlog/spdlog.h>
#include <fmt/core.h>
#include <nlohmann/json.hpp>
int main() {
spdlog::info("vcpkg basics demo");
nlohmann::json j = {{"name", "vcpkg"}, {"version", "1.0"}};
spdlog::info("JSON: {}", j.dump());
return 0;
}
7.2 Example 2: Triplet + static linking
cmake -B build -S . \
-DCMAKE_TOOLCHAIN_FILE="$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake" \
-DVCPKG_TARGET_TRIPLET=x64-windows-static \
-DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
7.3 Example 3: Overlay + Manifest
my-project/
├── CMakeLists.txt
├── vcpkg.json
├── vcpkg-configuration.json
├── vcpkg/
├── my-ports/
│ └── internal-lib/
│ ├── vcpkg.json
│ └── portfile.cmake
└── src/
└── main.cpp
vcpkg-configuration.json:
{
"default-registry": {
"kind": "git",
"repository": "https://github.com/microsoft/vcpkg",
"baseline": "a1b2c3d4e5f6..."
},
"overlay-ports": [./my-ports]
}
8. Common errors and fixes
Error 1: “Could not find a package configuration file provided by ‘fmt’”
CMake Error: Could not find a package configuration file provided by "fmt"
Cause: CMAKE_TOOLCHAIN_FILE missing, or vcpkg has not built the package yet.
Fix:
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE="$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake"
rm -rf build
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=...
Error 2: “Port xxx is not in the baseline”
Error: Could not find a version that satisfies the requirement ...
Cause: builtin-baseline is too old, or the package is unavailable at that baseline.
Fix:
cd vcpkg
git pull
git rev-parse HEAD
{
"builtin-baseline": "latest_commit_hash"
}
Error 3: “Building package xxx failed”
Building package spdlog:x64-linux failed
Cause: Compile errors while building the port, missing dependencies, network issues, etc.
Fix:
export VCPKG_VERBOSE=1
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=...
./vcpkg install spdlog --debug
Error 4: “A suitable version of cmake was not found”
Error: vcpkg was unable to find the version of cmake in your PATH
Cause: CMake missing from PATH or too old (use 3.20+).
Fix:
which cmake
cmake --version
Error 5: “undefined reference” / link errors
undefined reference to `spdlog::...'
Cause: Missing target_link_libraries, or mixed static/dynamic linking.
Fix:
find_package(spdlog CONFIG REQUIRED)
target_link_libraries(my-app PRIVATE spdlog::spdlog)
Error 6: C++ standard mismatch
error: #error "spdlog requires C++17 or later"
Cause: Project builds as C++14 while a dependency requires C++17.
Fix:
target_compile_features(my-app PRIVATE cxx_std_17)
# or
set(CMAKE_CXX_STANDARD 17)
Error 7: Overlay port not found
Error: Could not find port internal-lib
Cause: Wrong VCPKG_OVERLAY_PORTS path or invalid port directory layout.
Fix:
ls -la my-ports/internal-lib/vcpkg.json
cmake -B build -S . \
-DVCPKG_OVERLAY_PORTS="$(pwd)/my-ports"
Error 8: vcpkg.lock vs vcpkg.json conflict
Error: Version conflict: lock file expects [email protected] but vcpkg.json allows 10.2.0
Cause: vcpkg.json changed without regenerating the lock.
Fix:
rm vcpkg.lock
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=...
# Commit the new vcpkg.lock
Error 9: CI-only “Could NOT find”
Cause: Relying on Classic-mode global installs locally; CI lacks those packages.
Fix: Switch to Manifest mode, declare deps in vcpkg.json, pass CMAKE_TOOLCHAIN_FILE.
Error 10: “vcpkg integrate install” does not help
Cause: integrate install targets Classic mode. Manifest mode requires an explicit CMAKE_TOOLCHAIN_FILE at configure time.
Fix: Always pass -DCMAKE_TOOLCHAIN_FILE=.../vcpkg.cmake when configuring CMake.
9. Best practices
1. Prefer Manifest mode
- Keep
vcpkg.jsonat the repo root and commit it. - Treat Classic mode as legacy; new projects should use Manifest.
2. Pin builtin-baseline
- Use a specific registry commit for reproducibility.
- Test upgrades on a branch before merging to main.
3. Commit vcpkg.lock
- Lets the whole team resolve the same versions.
- After dependency edits, refresh the lock, test, and commit.
4. vcpkg submodule
git submodule add https://github.com/Microsoft/vcpkg.git vcpkg
- Pins vcpkg next to your code.
- In CI, check out with
submodules: recursive.
5. find_package in CONFIG mode
find_package(fmt CONFIG REQUIRED)
target_link_libraries(my-app PRIVATE fmt::fmt)
- Most vcpkg ports ship Config packages; passing
CONFIGis recommended.
6. Minimize dependencies
- Add only what you need.
- Header-only libraries (e.g. nlohmann-json) avoid extra compile steps.
7. Cache in CI
- Cache
buildtrees,packages,downloads. - Key caches with
hashFiles('vcpkg.json', 'vcpkg.lock').
8. CMake Presets
- Share
CMakePresets.jsonfor consistent developer and CI settings. - Example:
cmake --preset vcpkg-default.
10. Production patterns
Pattern 1: Multi-platform CI matrix
# .github/workflows/build.yml
strategy:
matrix:
include:
- os: ubuntu-latest
triplet: x64-linux
- os: windows-latest
triplet: x64-windows-static
- os: macos-latest
triplet: arm64-osx
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Configure
run: |
cmake -B build -S . \
-DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake \
-DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }}
Pattern 2: Cache vcpkg in CI
- name: Cache vcpkg
uses: actions/cache@v4
with:
path: |
${{ github.workspace }}/vcpkg/buildtrees
${{ github.workspace }}/vcpkg/packages
${{ github.workspace }}/vcpkg/downloads
key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json', 'vcpkg.lock') }}
restore-keys: vcpkg-${{ runner.os }}-
Pattern 3: CMakePresets.json
{
"version": 3,
"configurePresets": [
{
"name": "vcpkg-default",
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake"
}
},
{
"name": "vcpkg-static",
"inherits": "vcpkg-default",
"cacheVariables": {
"VCPKG_TARGET_TRIPLET": "x64-windows-static"
}
}
]
}
Pattern 4: Dependency update workflow
1. Development: periodically bump baseline, test locally
2. Staging: after updates, run full CI
3. Production: commit vcpkg.lock to freeze versions
Pattern 5: Shared internal overlays
Company repo: company/vcpkg-ports
Per project:
-DVCPKG_OVERLAY_PORTS="$(pwd)/../vcpkg-ports"
or add vcpkg-ports as another submodule
11. Implementation checklist
vcpkg basics
- Install vcpkg (bootstrap complete)
- Add vcpkg as a submodule (recommended)
- Set CMAKE_TOOLCHAIN_FILE
- Manifest mode + vcpkg.json
Versioning
- Pin builtin-baseline
- Commit vcpkg.lock to Git
- Use version constraints (
version>=,version<) where needed
Triplets
- Use static triplets (e.g. x64-windows-static) when shipping static binaries
- Align triplet across the team
Overlays (internal libraries)
- Create
my-ports/layout - Set VCPKG_OVERLAY_PORTS or vcpkg-configuration.json
CI/CD
-
submodules: recursive - Cache vcpkg buildtrees, packages, downloads
- Include
hashFiles('vcpkg.json', 'vcpkg.lock')in cache keys
Frequently asked questions (FAQ)
Q. Classic mode vs Manifest mode?
A. Classic installs packages globally with vcpkg install. Manifest declares dependencies in project vcpkg.json and installs during CMake configure. For reproducible builds, prefer Manifest.
Q. Do I have to pass CMAKE_TOOLCHAIN_FILE every time?
A. Put it in CMakePresets.json under cacheVariables and run cmake --preset vcpkg-default. IDEs (CLion, VS) can use the same preset.
Q. Must I commit vcpkg.lock?
A. If the team should build identical dependency versions, commit it. It regenerates when dependencies change—retest and commit together.
Q. How do I consume an internal library with vcpkg?
A. Create an overlay port: my-ports/your-lib/ with vcpkg.json and portfile.cmake, then pass -DVCPKG_OVERLAY_PORTS. See Creating vcpkg packages for details.
Q. CI is too slow.
A. Cache vcpkg buildtrees, packages, and downloads with actions/cache. Key with hashFiles('vcpkg.json', 'vcpkg.lock') so caches invalidate when dependencies change.
Q. When should I update builtin-baseline?
A. When you need security fixes, bug fixes, or newer port versions. Test on a branch, run the full suite, then merge.
One-line summary: Manifest mode + vcpkg.json + CMAKE_TOOLCHAIN_FILE solves most C++ dependency management pain points.
Next: [C++ #53-3] Advanced vcpkg | Manifest, triplet, overlay, binary cache
Previous: [C++ #53-2] Visual Studio C++ complete guide
Related posts
- C++ Advanced vcpkg | Manifest, triplet, overlay, binary cache
- C++ Creating vcpkg packages | Ports, build, and shipping [#53-3]
- C++ Conan basics | Install, conanfile, profiles, CMake [#53-4]
- CMake intro | Build automation when you have many files (CMakeLists.txt basics)
- C++ Conan guide | Modern C++ package management