02 — Conan 2.x and vcpkg¶
Both are good in 2026. Both are alive. Neither has “won”. Conan 2.x (major rewrite released January 2023, mature by 2025) is the choice when you need enterprise-grade cross-compilation, private registries, and per-configuration binary caching. vcpkg (Microsoft, since 2016, manifest mode since 2020) is the choice when you want the smallest possible on-ramp and a single Git repo that just builds on
cmake --preset default. You are going to learn both, because in studies and in real jobs you will see both, and “I only know one” is a bad answer.
This file is short and prescriptive: enough to get you a working fmt dependency both ways, and enough judgment to pick the right one for a future project. Both examples target CMake integration — you never pass their generated flags to g++ by hand.
1. Conan 2.x — concept model¶
Conan is written in Python and installs via pip install conan. It has:
A recipe for each package (Python file
conanfile.py) that knows how to build it from source or fetch a prebuilt binary.A profile describing your build environment (compiler,
libcxx,build_type,arch).A remote (default:
conancenter, mirrors ConanCenter) that stores recipes and prebuilt binaries keyed bysettings + options + dependencies.A cache at
~/.conan2/holding downloaded recipes and per-configuration binary artifacts.
The consumer file in your project is conanfile.txt (simple) or conanfile.py (programmable). For 90% of projects, start with conanfile.txt.
conanfile.txt¶
[requires]
fmt/10.2.1
spdlog/1.14.1
googletest/1.15.2
benchmark/1.9.0
[generators]
CMakeDeps
CMakeToolchain
[layout]
cmake_layout
First-time setup¶
pip install --user conan
conan profile detect --force # writes a default profile at ~/.conan2/profiles/default
Edit ~/.conan2/profiles/default and confirm it says compiler.cppstd=20 (add it if not):
[settings]
arch=armv8
build_type=Release
compiler=apple-clang
compiler.cppstd=20
compiler.libcxx=libc++
compiler.version=15
os=Macos
Install + build¶
conan install . --output-folder=build --build=missing
cmake --preset conan-release # Conan emits this preset automatically
cmake --build --preset conan-release
--build=missing says “if a matching binary isn’t in the remote for my exact settings, build from source”. Set -s build_type=Debug (or profiles) to get a Debug variant cached separately.
CMake side¶
Conan’s CMakeToolchain generator writes a conan_toolchain.cmake and CMake presets that point at it. Your CMakeLists.txt just does:
find_package(fmt CONFIG REQUIRED)
target_link_libraries(mynn PRIVATE fmt::fmt)
No Conan-specific lines in CMakeLists.txt. That’s the modern separation: Conan populates CMAKE_PREFIX_PATH, CMake handles the rest.
When Conan shines¶
Multiple simultaneous build configurations in one cache (Debug + Release + ASan + cross-compiled for aarch64) each with their own binary artifact.
Private artifactory (JFrog Artifactory, Conan Server) for proprietary packages — first-class in Conan.
Cross-compilation (
hostandbuildprofiles) is more explicit and reliable.Custom recipes (
conanfile.py) are just Python; expressive, testable.
2. vcpkg (manifest mode) — concept model¶
vcpkg is a C++ tool (Microsoft, MIT license) bootstrapped from a Git repo. It has:
A port for each package (a
portfile.cmake+vcpkg.json) checked into themicrosoft/vcpkgGit repo.Manifest mode: your project has a
vcpkg.jsonat its root listing dependencies. vcpkg installs them into a per-projectvcpkg_installed/directory. This is the mode you want; classic mode (global installs) is legacy.Binary caching: on by default from GitHub Actions cache, NuGet, or a local dir. Same source → same binary re-used.
Baseline pinning:
"builtin-baseline"is a Git SHA of the vcpkg repo. Same SHA on every machine = same package versions. Reproducible.
First-time setup (once per machine)¶
git clone https://github.com/microsoft/vcpkg.git ~/vcpkg
~/vcpkg/bootstrap-vcpkg.sh
export VCPKG_ROOT=~/vcpkg
vcpkg.json at project root¶
{
"name": "mynn",
"version-string": "0.1.0",
"dependencies": [
"fmt",
"spdlog",
{ "name": "gtest", "version>=": "1.15.2" },
"benchmark"
],
"builtin-baseline": "<full-git-sha-of-vcpkg-repo>"
}
Get the current baseline via cd ~/vcpkg && git rev-parse HEAD and paste it in. Refresh it deliberately when you want new package versions.
Configure with the vcpkg toolchain¶
cmake -B build -S . \
-DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake \
-DCMAKE_BUILD_TYPE=Release
cmake --build build
Or in CMakePresets.json:
{
"name": "vcpkg",
"inherits": "base",
"toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
"cacheVariables": { "CMAKE_BUILD_TYPE": "Release" }
}
CMake side¶
find_package(fmt CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)
target_link_libraries(mynn PRIVATE fmt::fmt spdlog::spdlog)
Identical to Conan on the CMake side. Both package managers deliberately hand off to CMake’s find_package(... CONFIG) route.
When vcpkg shines¶
Onboarding: single toolchain flag, and
cmakehandles the rest. New teammates go fromgit cloneto green build in one step.Ports maintained by Microsoft + community: broadest coverage of Windows-native and cross-platform libraries.
Binary caching in GHA is trivial:
actions/cacheonvcpkg_installed+ baseline SHA and you’re done.Baseline as Git SHA = reproducibility by construction.
3. Head-to-head¶
Dimension |
Conan 2.x |
vcpkg (manifest) |
|---|---|---|
Package count (2026) |
~2000 in ConanCenter |
~2500 in vcpkg registry |
Written in |
Python |
C++ |
Install path |
|
|
Reproducibility mechanism |
|
|
CMake integration |
|
Single |
Multiple configs cached |
First-class, per-config binaries |
Yes, with binary caching |
Private registries |
First-class (Artifactory) |
Registry overlays; less polished |
Cross-compilation |
Explicit host/build profiles |
Triplets ( |
Ecosystem lean |
Enterprise / regulated |
Microsoft / cross-platform hobbyist |
Learning curve |
Steeper (profiles, settings, options) |
Gentler (one JSON, one toolchain flag) |
4. Which one should you pick?¶
Honest 2026 guidance:
Personal projects, portfolio, ML/data code: vcpkg. Faster onboarding, JSON manifest, CI matrix trivially reproducible. Your ONNX Runtime / Arrow / Eigen dependencies are all there.
Enterprise C++ shops with monorepos, cross-compilation, or private packages: Conan 2. Zoho, if it standardizes on C++ package management on the ML platform side, is more likely to reach for Conan for the private-registry + profiles story.
Rule for Phase 4: your reference template ships both flavors on different branches (
main= vcpkg,conan= Conan). You want both on your resume; the delta between them is one file (vcpkg.json↔conanfile.txt) plus toolchain wiring.
5. Traps and gotchas¶
Conan
Forgetting
compiler.cppstd=20in your profile → your C++20 code silently compiles as C++17. Check withconan profile show.Not pinning
--build=missingbehavior → sudden multi-hour builds when a new setting doesn’t match the cache.libcxxmismatch on macOS (libc++vslibstdc++) causing linker errors — profile-level fix.
vcpkg
Forgetting to update
builtin-baseline→ you stay on stale package versions across the whole team.Vendored
vcpkg_installed/in the repo (don’t). It goes in.gitignore.On CI, forgetting to cache
vcpkg_installed/→ 20-minute cold builds every run. Useactions/cachekeyed on your baseline SHA +vcpkg.jsonhash.VCPKG_ROOTunset in CI — preset silently falls back or errors. Set it explicitly.
Both
Don’t check package-manager output directories into Git.
Don’t shadow package-manager-provided targets with your own
add_library(fmt ...). Namespaces (fmt::fmt) protect you — use them.
6. What most people get wrong¶
They pick a package manager, get one dep working, and never look at the second. Then, six months later, at a new job that uses the other one, they can’t tell the story of why a find_package(fmt CONFIG REQUIRED) call resolves. Do both. It’s a weekend. The concepts — recipes/ports, per-config caches, toolchain integration — are the same. Only the syntax differs.
Also wrong: reaching for FetchContent for every dep because it “just works”. Fine for gtest and benchmark (tiny, header-heavy). Terrible for Arrow, Eigen, LibTorch — you’ll rebuild multi-gigabyte deps from scratch on every clean checkout. Use a real package manager for real deps.