본문으로 건너뛰기
Previous
Next
C++ Package Management: vcpkg & Conan — Escaping External

C++ Package Management: vcpkg & Conan — Escaping External

C++ Package Management: vcpkg & Conan — Escaping External

이 글의 핵심

C++ package management in practice: escape dependency hell with vcpkg and Conan [#40-1]. Dependency hell, using vcpkg, concepts, code, and production patterns.

Introduction: “How do I add this library?”

Path hell just to include a header

If you already covered CMake and package managers overview, part 40 automates the full project lifecycle. The first step is external libraries. Manually downloading, building, and aligning include paths diverges environments across teammates and tangles versions. vcpkg and Conan are tools where you declare packages and versions, and they fetch, build, and wire paths for your build. vcpkg is Microsoft-led and integrates well with CMake; Conan excels at cross-platform and multi-compiler workflows. Both underpin reproducible builds (same source and settings → same result everywhere). Other ecosystems solve the same problem with Python pip·uv·Poetry·npm·Go modules·Rust Cargo; the build generator is often CMake. See build system comparison for the big picture, and vcpkg·Conan for deep dives. This article covers:

  • vcpkg: install, Manifest mode, CMake integration, triplet
  • Conan: conanfile.txt / conanfile.py, profiles, CMake integration
  • Problem scenarios: dependency hell, build failures, version conflicts
  • Complete example: project layout you can copy and build
  • Common errors and fixes
  • Best practices and production patterns
  • Selection guide: by project size, team, and CI

Analogy

Build·test·deploy pipelines resemble a factory QC line: same inputs should yield same outputs; Sanitizers and static analysis act like pre-shipment defect checks.

Practical note: This article is based on real issues and fixes from large C++ projects. It includes production pitfalls and debugging tips rarely covered in books or docs alone.

Table of contents

  1. Problem scenario: dependency hell
  2. Using vcpkg
  3. Using Conan
  4. vcpkg vs Conan comparison
  5. Common errors and fixes
  6. Best practices
  7. Production patterns
  8. Summary

1. Problem scenario: dependency hell

What you actually see

"Teammate A installed under /usr/local, I used /opt."
"It built on Windows but fails on Linux."
"I built with spdlog 1.10, but the server has 1.8."
"find_package(Boost) fails. Where is Boost?"
"Dependency A wants C++17, dependency B wants C++14 — conflict."

Root causes

  1. Path mismatch: different install paths per developer
  2. Version mismatch: dev / CI / prod library versions differ
  3. Platform differences: Windows / Linux / macOS build settings differ
  4. Dependency trees: A needs B, B needs C — hard to align manually

More scenarios

Scenario 5: new hire onboarding

"I want to build too — where do I get Boost and OpenSSL?"
"I installed a different version — why does only my machine error?"

With manual installs, onboarding can take hours. With vcpkg/Conan plus a one-line README, cmake -B build -S . can be enough. Scenario 6: CI-only failures

"It works locally but fails only on GitHub Actions."
"After clearing the cache the build broke."

If you depend on libraries installed only locally, CI fails. Manifest mode or a conanfile pins dependencies in the repo so CI matches. Scenario 7: complex dependency trees

Project → A → B → C
        → D → C

If A and D both need C but different versions, you get “which C?”. vcpkg and Conan perform dependency resolution for you.

Direction

Declarative dependency management: list required packages and versions in vcpkg.json or conanfile.txt, and the tool downloads, builds, and configures paths. Version-controlled with code → reproducible builds.

flowchart LR
  subgraph before[Manual management]
    B1[Dev A] --> B2[Path A]
    B3[Dev B] --> B4[Path B]
    B5[CI] --> B6[Path C]
    B2 -.->|mismatch| B4
    B4 -.->|mismatch| B6
  end
  subgraph after[Package manager]
    A1[vcpkg.json] --> A2[Same dependencies]
    A2 --> A3[Dev A]
    A2 --> A4[Dev B]
    A2 --> A5[CI]
  end

Caveat: triplet/profile differences (OS·architecture) remain — document the CI triplet.

2. Using vcpkg

Installation

# Linux/macOS: clone vcpkg then bootstrap
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
# Windows (PowerShell)
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat

After install: the vcpkg binary is ./vcpkg (Unix) or vcpkg.exe (Windows). Add to PATH or invoke by absolute path.

In Manifest mode, put vcpkg.json at the project root. When you configure CMake, vcpkg builds and installs declared packages. The “packages this project needs” list is version-controlled → better reproducibility.

Example layout

my-app/
├── CMakeLists.txt
├── vcpkg.json          # dependency manifest
└── src/
    └── main.cpp

vcpkg.json

{
  "name": "my-app",
  "version": "1.0.0",
  "description": "vcpkg Manifest mode example",
  "dependencies": [
    "fmt",
    "spdlog",
    {
      "name": "openssl",
      "version>=": "3.0.0"
    }
  ],
  "builtin-baseline": "a1b2c3d4e5f6"
}

Notes:

  • dependencies: required packages; bare strings use latest compatible versions
  • version>=: minimum version
  • builtin-baseline: commit hash of the vcpkg ports tree — recommended for reproducible builds

CMakeLists.txt

cmake_minimum_required(VERSION 3.20)
project(my-app VERSION 1.0.0 LANGUAGES CXX)
# vcpkg toolchain: use vcpkg packages when configuring CMake
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake"
    CACHE STRING "Vcpkg toolchain file")
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
)
# C++17
target_compile_features(my-app PRIVATE cxx_std_17)

Key point: pointing CMAKE_TOOLCHAIN_FILE at vcpkg’s vcpkg.cmake makes find_package use vcpkg-installed paths automatically.

main.cpp (runnable example)

#include <spdlog/spdlog.h>
#include <fmt/core.h>
int main() {
    spdlog::info("Built with vcpkg Manifest mode");
    spdlog::info("fmt example: {}", fmt::format("Hello, {}!", "vcpkg"));
    return 0;
}

Build steps

# 1. If vcpkg is a git submodule in the project
git submodule add https://github.com/Microsoft/vcpkg.git vcpkg
# 2. CMake configure (vcpkg installs dependencies automatically)
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE="$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake"
# 3. Build
cmake --build build
./build/my-app

Expected output: lines like [info] Built with vcpkg Manifest mode.

Triplet

A triplet selects platform·architecture·linking flavor.

TripletDescription
x64-windowsWindows 64-bit
x64-windows-staticWindows 64-bit static linking
x64-linuxLinux 64-bit
arm64-osxmacOS Apple Silicon
x64-osxmacOS Intel
# Use a specific triplet
cmake -B build -S . \
  -DCMAKE_TOOLCHAIN_FILE="$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake" \
  -DVCPKG_TARGET_TRIPLET=x64-windows-static

Running multiple triplets in CI enables multi-platform builds.

Classic mode (legacy)

Classic mode uses vcpkg install for a global install. It is weaker for reproducibility than Manifest mode — prefer Manifest for new projects.

vcpkg install fmt spdlog
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake

vcpkg overlays (internal ports)

Use an overlay when a library is missing from the official registry or you maintain a patched port in-house.

# Overlay path
cmake -B build -S . \
  -DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake \
  -DVCPKG_OVERLAY_PORTS=/path/to/my-ports

Overlay layout:

my-ports/
└── my-internal-lib/
    ├── portfile.cmake
    └── vcpkg.json

Version constraints in vcpkg.json

You can pin ranges precisely in vcpkg.json.

{
  "dependencies": [
    "fmt",
    {
      "name": "spdlog",
      "version>=": "1.11.0",
      "version<": "2.0.0"
    },
    {
      "name": "openssl",
      "version>=": "3.0.0",
      "platform": "!windows"
    }
  ]
}
  • version>=: minimum version
  • version<: exclusive upper bound
  • platform: add the dependency only on selected platforms (!windows = not on Windows)

3. Using Conan

Installation

# Conan 2.x (pip recommended)
pip install conan
# Check version
conan --version

conanfile.txt (simple projects)

conanfile.txt lists package names and versions in a minimal format.

Layout

my-conan-app/
├── CMakeLists.txt
├── conanfile.txt
└── src/
    └── main.cpp

conanfile.txt

[requires]
fmt/10.1.1
spdlog/1.12.1
[generators]
CMakeDeps
CMakeToolchain

Notes:

  • [requires]: name/version, e.g. fmt/10.1.1
  • [generators]: CMakeDeps (for find_package), CMakeToolchain (toolchain file)

Install and build

# 1. Install Conan deps (generates CMake files under build/)
conan install . --output-folder=build --build=missing
# 2. CMake configure (use Conan toolchain)
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake
# 3. Build
cmake --build build
./build/my-conan-app

--build=missing: build from source when a package is missing in the local cache.

CMakeLists.txt

cmake_minimum_required(VERSION 3.20)
project(my-conan-app VERSION 1.0.0 LANGUAGES CXX)
add_executable(my-conan-app src/main.cpp)
find_package(fmt REQUIRED)
find_package(spdlog REQUIRED)
target_link_libraries(my-conan-app PRIVATE
    fmt::fmt
    spdlog::spdlog
)
target_compile_features(my-conan-app PRIVATE cxx_std_17)

Important: after conan install, pass CMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake so find_package resolves Conan packages.

main.cpp

#include <spdlog/spdlog.h>
#include <fmt/core.h>
int main() {
    spdlog::info("Built with Conan");
    spdlog::info("fmt: {}", fmt::format("Hello, Conan!"));
    return 0;
}

conanfile.py (advanced)

Use conanfile.py for finer control over build scripts, options, and requirements.

from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMakeDeps, cmake_layout
from conan.tools.build import check_min_cppstd
class MyAppConan(ConanFile):
    settings = "os", "compiler", "build_type", "arch"
    generators = "CMakeDeps", "CMakeToolchain"
    def layout(self):
        cmake_layout(self)
    def requirements(self):
        self.requires("fmt/10.1.1")
        self.requires("spdlog/1.12.1")
    def configure(self):
        check_min_cppstd(self, "17")
    def generate(self):
        deps = CMakeDeps(self)
        deps.generate()
        tc = CMakeToolchain(self)
        tc.generate()

Profiles

A profile defines compiler, build type (Release/Debug), and architecture.

# Show default profile
conan profile show default
# Profile paths
# Linux: ~/.conan2/profiles/default
# Windows: C:\Users\<user>\.conan2\profiles\default

Example profile (~/.conan2/profiles/default):

[settings]
os=Linux
os_build=Linux
arch=x86_64
arch_build=x86_64
compiler=gcc
compiler.version=12
compiler.libcxx=libstdc++11
build_type=Release

Share profiles across team and CI for aligned environments.

# Use a specific profile
conan install . --output-folder=build --build=missing -pr=./conanprofile

Conan options

Some packages expose options to change build behavior.

# conanfile.txt
[requires]
spdlog/1.12.1
[options]
spdlog:shared=False
spdlog:wchar_support=True

In conanfile.py, use default_options or set options in configure.

def configure(self):
    self.options[spdlog].shared = False

Searching recipes

# Search Conan Center
conan search spdlog --remote=conancenter
# List available versions
conan search spdlog --remote=conancenter -q="*"

Conan build flow

sequenceDiagram
    participant Dev as Developer
    participant Conan as Conan
    participant Cache as Cache
    participant CMake as CMake
    Dev->>Conan: install
    Conan->>Cache: package?
    alt exists
        Cache-->>Conan: provide
    else missing
        Conan->>Conan: build
        Conan->>Cache: store
    end
    Conan->>Dev: toolchain
    Dev->>CMake: configure
    CMake->>Conan: find_package
    Conan-->>CMake: pass paths

4. vcpkg vs Conan comparison

TopicvcpkgConan
IntegrationOne CMake toolchain lineconan install + CMakeToolchain
Package countOfficial registry 2000+Conan Center 1000+
Custom buildsPort patches·overlaysFull control via conanfile.py
Multi-compilerVia tripletVia profile
Internal packagesOverlay portsPrivate repos (Artifactory, etc.)
CacheLocal buildtreesConan cache (~/.conan2)

Selection guide

  • vcpkg: Windows·Visual Studio·CMake-first, Microsoft ecosystem
  • Conan: exotic compilers, distributing internal libraries as packages, stronger cross-platform needs If you already standardized on vcpkg, keep going; if internal libraries ship as Conan packages, Conan may fit better. Pick one convention for the team and CI and stick to it.

5. Common errors and fixes

vcpkg

Error 1: “Could not find a package configuration file”

CMake Error: Could not find a package configuration file provided by "fmt"

Cause: CMAKE_TOOLCHAIN_FILE not set, or vcpkg has not built that package yet. Fix:

# 1. Verify toolchain file
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake
# 2. If vcpkg.json exists but packages are missing, reconfigure from scratch
# (Manifest mode installs during CMake configure)
rm -rf build
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=...

Error 2: “A suitable version of cmake was not found”

Error: vcpkg was unable to find the version of cmake in your PATH

Cause: vcpkg uses CMake internally; missing from PATH or too old. Fix:

# CMake 3.20+ recommended
which cmake
cmake --version
# Add to PATH or point vcpkg at the cmake binary

Error 3: “Port xxx is not in the baseline”

Error: Could not find a version that satisfies the requirement ...

Cause: builtin-baseline is stale or the package is not in that baseline. Fix:

# Refresh baseline
cd vcpkg
git pull
git rev-parse HEAD  # put this hash in vcpkg.json builtin-baseline
{
  "builtin-baseline": "latest_commit_hash"
}

Conan

Error 4: “fmt/10.1.1: NotFound”

ERROR: Package 'fmt/10.1.1' not found

Cause: version not on Conan Center or remote misconfiguration. Fix:

# List available versions
conan search fmt --remote=conancenter
# Check remotes
conan remote list
# conancenter should be present

Error 5: “find_package(fmt) failed”

Cause: CMAKE_TOOLCHAIN_FILE not set to Conan’s conan_toolchain.cmake. Fix:

# Run conan install first
conan install . --output-folder=build --build=missing
# Pass toolchain to CMake (adjust path)
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=$(pwd)/build/conan_toolchain.cmake

Error 6: “Binary not found” / “Can’t find a compatible binary”

ERROR: Missing prebuilt package for 'fmt/10.1.1'

Cause: no prebuilt binary for your compiler/arch/settings. Fix:

# Build from source
conan install . --output-folder=build --build=missing
# Build only one package
conan install . --output-folder=build --build=fmt

Shared

Error 7: “C++ standard mismatch”

error: #error "spdlog requires C++17 or later"

Cause: project builds as C++14 but a dependency needs C++17. Fix:

# In CMakeLists.txt
target_compile_features(myapp PRIVATE cxx_std_17)
# or
set(CMAKE_CXX_STANDARD 17)

Cause: wrong link of header-only libs, or static/dynamic mix. Fix:

  • vcpkg: unify triplet, e.g. x64-windows-static
  • Conan: same build_type in profile for everyone

Error 9: vcpkg “Building package …failed”

Building package xxx:x64-linux failed

Cause: compile error while building port, missing system deps, etc. Fix:

# Verbose logs
export VCPKG_VERBOSE=1
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=...
# Try building one package manually
./vcpkg install xxx --debug

Error 10: Conan “Version conflict”

ERROR: Conflict in spdlog/1.12.1: requirement 'fmt/9.x' conflicts with 'fmt/10.1.1'

Cause: transitive version clash — spdlog wants fmt 9.x but you require 10.1.1. Fix:

# Inspect the graph
conan graph info . --format=html
# Adjust versions in conanfile — e.g. align fmt with spdlog
[requires]
fmt/9.1.0
spdlog/1.12.1

Error 11: “conan_toolchain.cmake not found”

Cause: conan install not run, or --output-folder does not match CMake -B. Fix:

# Match output-folder and CMake build dir
conan install . --output-folder=build --build=missing
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake
# build/ must be the same directory

6. Best practices

vcpkg

  1. Use Manifest mode: vcpkg.json at repo root, committed to Git
  2. Pin builtin-baseline: use a specific ports commit for reproducibility
  3. Specify versions: prefer "version>=" and similar when possible
  4. Submodule vcpkg: git submodule add https://github.com/Microsoft/vcpkg.git vcpkg
  5. Cache in CI: cache vcpkg buildtrees to shorten CI time

Conan

  1. conanfile.txt or conanfile.py: choose by team complexity
  2. Share profiles: commit a conanprofile (or equivalent) to the repo
  3. Use lockfiles: conan lock create to pin the graph
  4. Cache ~/.conan2 in CI

Shared

  1. Minimize dependencies: add only what you need
  2. Pin versions in production where practical
  3. Document in README: vcpkg or Conan and exact build steps

Details

vcpkg: baseline update cadence

builtin-baseline points at a specific vcpkg ports commit. Updating periodically brings security and bug fixes, but to avoid surprise breakage, test on a branch before bumping baseline.

# Update baseline to latest vcpkg commit
cd vcpkg && git pull
git rev-parse HEAD  # write this into vcpkg.json

Conan: lockfiles

# Create conan.lock (pinned graph)
conan lock create .
# CI uses the same graph
conan install . --output-folder=build --lockfile=conan.lock --build=missing

Committing conan.lock keeps everyone on the same dependency versions.

find_package and CONFIG mode

Most vcpkg-installed packages ship Config packages. Prefer find_package(fmt CONFIG REQUIRED) over legacy Module mode when available.

# Recommended: CONFIG mode
find_package(fmt CONFIG REQUIRED)
target_link_libraries(myapp PRIVATE fmt::fmt)
# Module mode (older packages)
find_package(Boost REQUIRED COMPONENTS system)

7. Production patterns

Pattern 1: vcpkg Manifest in CI

# .github/workflows/build.yml (example)
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive  # includes vcpkg submodule
      - name: Configure CMake
        run: |
          cmake -B build -S . \
            -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake \
            -DCMAKE_BUILD_TYPE=Release
      - name: Build
        run: cmake --build build --config Release

Pattern 2: Conan lockfile for reproducibility

conan lock create .
conan install . --output-folder=build --lockfile=conan.lock --build=missing

Pattern 3: Multi-platform builds (vcpkg)

strategy:
  matrix:
    triplet: [x64-linux, x64-windows, arm64-osx]
steps:
  - run: |
      cmake -B build -S . \
        -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake \
        -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }}

Pattern 4: Private Conan remote

conan remote add company https://artifactory.company.com/artifactory/api/conan/conan-local
# In conanfile.txt
[requires]
company-internal-lib/1.0.0
fmt/10.1.1

Pattern 5: Docker with vcpkg

# Dockerfile example (vcpkg)
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y git cmake g++ build-essential
RUN git clone https://github.com/Microsoft/vcpkg.git /vcpkg && \
    /vcpkg/bootstrap-vcpkg.sh
ENV CMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake
WORKDIR /app
COPY . .
RUN cmake -B build -S . && cmake --build build

Pattern 6: Conan + Docker

# Dockerfile example (Conan)
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y python3-pip cmake g++ build-essential
RUN pip install conan
WORKDIR /app
COPY . .
RUN conan install . --output-folder=build --build=missing
RUN cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake && \
    cmake --build build

Pattern 7: CI cache for Conan and vcpkg

# vcpkg cache (GitHub Actions)
- name: Cache vcpkg
  uses: actions/cache@v4
  with:
    path: ${{ env.VCPKG_ROOT }}/buildtrees
    key: vcpkg-${{ runner.os }}-${{ hashFiles('**/vcpkg.json') }}
# Conan cache
- name: Cache Conan
  uses: actions/cache@v4
  with:
    path: ~/.conan2
    key: conan-${{ runner.os }}-${{ hashFiles('**/conanfile.txt', '**/conanfile.py') }}

Pattern 8: vcpkg + CMake Presets

CMakePresets.json shares one configure setup across the team.

{
  "version": 3,
  "configurePresets": [
    {
      "name": "vcpkg-default",
      "cacheVariables": {
        "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake"
      }
    }
  ]
}
cmake --preset vcpkg-default
cmake --build build

Pattern 9: Dependency update strategy

1. Dev: periodically bump vcpkg baseline or Conan versions
2. Staging: full test pass after bumps
3. Production: only validated versions in lockfile/baseline

8. Summary

  • vcpkg: Manifest mode + CMake toolchain → declarative deps and reproducible builds; strong on Windows·CMake.
  • Conan: conanfile + profiles for compiler and build type, CMake generators for integration; strong for cross-platform and internal packages.
  • Both reduce “dependency hell” through declaration and automation; pair with CI/CD·GitHub Actions (#40-2) for stable multi-OS builds.

Implementation checklist

  • Choose vcpkg or Conan per team convention
  • Declare deps with Manifest (vcpkg) or conanfile (Conan)
  • Set CMAKE_TOOLCHAIN_FILE correctly
  • Pin C++ standard (e.g. C++17)
  • Include build steps in CI
  • Document build steps in README

Posts that connect to this topic.

Practical checklist

What to verify when applying these ideas.

Before coding

  • Is this the right fix for the problem?
  • Can teammates understand and maintain it?
  • Does it meet performance needs?

While coding

  • Are compiler warnings cleared?
  • Are edge cases handled?
  • Is error handling appropriate?

At code review

  • Is intent clear?
  • Are tests sufficient?
  • Is it documented? Use this checklist to reduce mistakes and improve quality.

vcpkg, Conan, C++ package manager, CMake dependencies, reproducible build — searching these should surface useful material including this article.

FAQ

Q. When do I use this at work?

A. In any C++ project using third-party libraries (fmt, spdlog, OpenSSL, …) where you want identical builds for the whole team and CI. vcpkg or Conan remove manual installs and path hacks and integrate with CI/CD for reproducible builds.

Q. vcpkg or Conan?

A. Favor vcpkg for Windows·Visual Studio·Microsoft-centric workflows; consider Conan for unusual compilers, internal package distribution, or stronger cross-platform needs. If the team already picked one, standardize on it.

Q. What should I read first?

A. Follow Previous post links at the bottom of each article for order. See the C++ series index for the full path.

Q. Where to go deeper?


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.


이 글에서 다루는 키워드 (관련 검색어)

C++, vcpkg, Conan, package management, CMake, dependencies 등으로 검색하시면 이 글이 도움이 됩니다.