C++ Package Managers: Escaping Library Install Hell with vcpkg and Conan

Introduction: installing libraries is too hard

You tried to add the Boost library to a project, but you had to:

  • Download sources
  • Run build scripts
  • Configure header paths
  • Configure library paths
  • Deal with different settings per platform

It took a whole day.

Tools like vcpkg and Conan are package managers (they automate downloading, building, and path setup). You only declare dependencies (what you need—name and version), and they download, build, and wire paths for you. The whole team can use the same library versions, which reduces “it works on my PC but fails in CI” issues.

Below is a mermaid example.

flowchart LR
  subgraph manual[Manual install]
    M1[Download sources] --> M2[Build]
    M2 --> M3[Path setup]
    M3 --> M4[Per-platform settings]
  end
  subgraph pm[Package manager]
    P1[vcpkg.json / conanfile] --> P2[Automatic install]
    P2 --> P3[CMake integration]
  end

Fixing it with vcpkg: vcpkg install boost downloads, builds, and installs Boost in one step. After vcpkg integrate install, CMake discovers the vcpkg install path automatically. Then find_package(Boost REQUIRED) sets include and library paths, and target_link_libraries(myapp PRIVATE Boost::boost) links without hand-editing paths. That greatly reduces “a day to install Boost” situations.

Run the following in a terminal.

# After pasting: run from your vcpkg directory. After install, pass -DCMAKE_TOOLCHAIN_FILE=[vcpkg]/scripts/buildsystems/vcpkg.cmake to CMake
vcpkg install boost-asio

# Auto-detected in CMake (when toolchain is set)
# find_package(Boost REQUIRED)
# target_link_libraries(myapp PRIVATE Boost::boost)

Result: vcpkg downloads and builds Boost.Asio; if vcpkg list shows boost-asio, installation succeeded.

Runnable example (minimal C++ you can build after vcpkg is set up):

Example main implementation:

// After pasting: build with CMake using the vcpkg toolchain, or without deps: g++ -std=c++17 -o demo main.cpp && ./demo
#include <iostream>
int main() {
    std::cout << "After aligning the environment with vcpkg, build this project.\n";
    return 0;
}

After reading this post you will be able to:

  • Install libraries easily with vcpkg
  • Manage dependencies with Conan
  • Declare project dependencies explicitly
  • Build reproducibly in real projects

Practical experience: this article is based on problems and fixes from large C++ projects. It includes production pitfalls and debugging tips that books often skip.

Where dependency management hurts in practice

“It builds locally but fails in CI”

Symptom: Local builds succeed, but GitHub Actions or Jenkins reports find_package(fmt) failed.

Cause: Libraries you installed by hand locally are missing in CI. System packages (apt, brew) vary by platform and version.

Fix: Declare dependencies in vcpkg.json or a Conan file, and build in CI with the same toolchain/profile.

“A library version changed and the build broke”

Symptom: Yesterday’s project fails today after conan install.

Cause: No version range was pinned, so the latest version was pulled; API changes broke compatibility.

Fix: Pin with vcpkg builtin-baseline, or pin explicit versions in Conan (e.g. fmt/9.1.0).

Symptom: Errors like undefined reference to 'fmt::v9::format(...)'.

Cause: find_package succeeded but target_link_libraries is missing a target, or you mixed static/dynamic (e.g. Release-built fmt linked into Debug).

Fix: Use target_link_libraries(myapp PRIVATE fmt::fmt) explicitly; align build type in triplet/profile.

“The dependency graph is a mess”

Symptom: Component A needs Boost 1.70 and B needs 1.80, and you must link both.

Cause: Global installs or mixing multiple package managers.

Fix: Isolate per project with manifest mode (vcpkg.json) or a Conan file; use one version per project.

“Install takes forever”

Symptom: vcpkg install or conan install runs 30+ minutes.

Cause: Building from source every time with no binary cache; CI always clean-builds.

Fix: Use vcpkg binary cache and Conan remotes; split dependency caching in CI.


Installing vcpkg and wiring it into CMake

Installation

Clone the vcpkg Git repository, then run the bootstrap script to build the vcpkg executable. On Linux/macOS use ./bootstrap-vcpkg.sh; on Windows use .\bootstrap-vcpkg.bat. Run vcpkg from that directory or add it to PATH. One install can serve many projects.

Run the following in a terminal.

# After pasting: run outside the project. After completion you get vcpkg.exe (Windows) or vcpkg (Linux/Mac)
# Linux/Mac
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh

# Windows (PowerShell or cmd)
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat

Result: The vcpkg (or vcpkg.exe) binary is created; check with ./vcpkg version.

Basic usage

vcpkg search json finds packages whose names contain “json”; vcpkg install nlohmann-json installs one. List packages with vcpkg list; remove with vcpkg remove nlohmann-json. Port names can differ from upstream (e.g. nlohmann-json), so use search to confirm the exact name.

Run the following in a terminal.

# Search
vcpkg search json

# Install
vcpkg install nlohmann-json

# List installed
vcpkg list

# Remove
vcpkg remove nlohmann-json

CMake integration

vcpkg integrate install registers a user-wide integration script. When you configure CMake with -DCMAKE_TOOLCHAIN_FILE=[vcpkg root]/scripts/buildsystems/vcpkg.cmake, find_package discovers vcpkg-installed packages and target_link_libraries pulls include and link paths. Your CMakeLists.txt can stay at “find_package + target_link_libraries” without hard-coded paths.

Run the following in a terminal.

# User integration
vcpkg integrate install

# Auto in CMakeLists.txt
# find_package(nlohmann_json REQUIRED)
# target_link_libraries(myapp PRIVATE nlohmann_json::nlohmann_json)

Manifest mode, triplets, and version pinning

vcpkg.json (manifest mode)

In manifest mode, put vcpkg.json at the project root and declare dependencies. Use a dependencies array of package names as strings, or objects with name and features (e.g. Boost system, filesystem). When you run cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=…, vcpkg reads vcpkg.json and installs what is needed. Teammates and CI get the same graph from one file, improving reproducibility.

Example manifest:

{
  "name": "myproject",
  "version": "1.0.0",
  "dependencies": [
    "fmt",
    "spdlog",
    "nlohmann-json",
    {
      "name": "boost",
      "features": ["system", "filesystem"]
    }
  ]
}

Usage:

# From the directory that contains vcpkg.json
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=[vcpkg root]/scripts/buildsystems/vcpkg.cmake
cmake --build build

Triplets (platform selection)

A triplet names how vcpkg builds: platform, architecture, and linkage. x64-windows is Windows 64-bit, x64-linux Linux 64-bit, x64-osx macOS. x64-windows-static builds static libraries when you want a single executable with everything linked in. You can set triplets per CI vs local (e.g. CI x64-linux, dev x64-windows).

Run the following in a terminal.

# Windows 64-bit
vcpkg install fmt:x64-windows

# Linux 64-bit
vcpkg install fmt:x64-linux

# macOS
vcpkg install fmt:x64-osx

# Static libs
vcpkg install fmt:x64-windows-static

Version pinning

Use version>= for a minimum version and builtin-baseline with a commit date or hash from the vcpkg repo to lock ports to that snapshot. That avoids “9.1.0 today, 9.2.0 tomorrow, build breaks” drift. A shared baseline aligns the whole team and CI.

Example manifest:

{
  "name": "myproject",
  "version": "1.0.0",
  "dependencies": [
    {
      "name": "fmt",
      "version>=": "9.1.0"
    }
  ],
  "builtin-baseline": "2023-04-15"
}

A complete vcpkg project

Project layout

myapp-vcpkg/
├── CMakeLists.txt
├── vcpkg.json
├── src/
│   └── main.cpp
└── tests/
    └── test_main.cpp

vcpkg.json (dependency declaration)

Example manifest:

{
  "name": "myapp-vcpkg",
  "version": "1.0.0",
  "description": "Example C++ project using vcpkg",
  "dependencies": [
    "fmt",
    "spdlog",
    {
      "name": "nlohmann-json",
      "version>=": "3.11.0"
    },
    {
      "name": "boost-asio",
      "features": [ssl]
    }
  ],
  "builtin-baseline": "a1c7f64"
}

CMakeLists.txt

CMake example:

cmake_minimum_required(VERSION 3.15)
project(MyAppVcpkg LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)

# With the vcpkg toolchain, find_package resolves vcpkg install paths
find_package(fmt REQUIRED)
find_package(spdlog REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(Boost REQUIRED COMPONENTS system)

add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE
    fmt::fmt
    spdlog::spdlog
    nlohmann_json::nlohmann_json
    Boost::system
)

src/main.cpp (runnable example)

#include <iostream>
#include <fmt/core.h>
#include <spdlog/spdlog.h>
#include <nlohmann/json.hpp>
#include <boost/asio.hpp>

int main() {
    spdlog::info("vcpkg example start");

    // fmt
    std::cout << fmt::format("Hello, {}!\n", "vcpkg");

    // nlohmann-json
    auto j = nlohmann::json::parse(R"({"name": "myapp", "version": 1})");
    std::cout << "JSON: " << j.dump() << "\n";

    // Boost.Asio (timer only)
    boost::asio::io_context io;
    boost::asio::steady_timer t(io, boost::asio::chrono::seconds(1));
    t.async_wait( { spdlog::info("timer done"); });
    io.run();

    return 0;
}

Build commands

Run the following in a terminal.

# Set VCPKG_ROOT to your vcpkg install path
export VCPKG_ROOT=/path/to/vcpkg

cmake -B build -S . \
  -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake

cmake --build build
./build/myapp

Installing Conan and wiring it into CMake

Installation

pip install conan

conan --version

Basic usage

conan profile detect —force detects your environment (OS, compiler, arch) and creates a default profile. In conanfile.txt, put packages under [requires], and add CMakeDeps and CMakeToolchain under [generators]. conan install . —build=missing installs dependencies and generates CMake files (e.g. conan_toolchain.cmake). —build=missing builds a recipe if it is not in the cache.

Run the following in a terminal.

conan profile detect --force

conan search fmt -r conancenter

conan install . --build=missing

conanfile.txt example

INI example:

[requires]
fmt/9.1.0
spdlog/1.11.0

[generators]
CMakeDeps
CMakeToolchain

[layout]
cmake_layout

CMake integration

Run conan install first so conan_toolchain.cmake appears under your build directory. Pass -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake at configure time so find_package(fmt) resolves Conan-installed packages. vcpkg can read a manifest from the toolchain alone; Conan usually requires conan install first—document that order in scripts or README.

CMake example:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(MyProject)

set(CMAKE_CXX_STANDARD 17)

# Conan toolchain (after conan install, e.g. build/conan_toolchain.cmake)
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_BINARY_DIR}/conan_toolchain.cmake" CACHE PATH "" FORCE)

find_package(fmt REQUIRED)
find_package(spdlog REQUIRED)

add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE
    fmt::fmt
    spdlog::spdlog
)

Build:

Run the following in a terminal.

# 1. Dependencies first
conan install . --build=missing

# 2. CMake
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake
cmake --build build

conanfile.py, options, settings, and profiles

conanfile.py

conanfile.py drives dependencies, build, and packaging in Python. In requirements(), call self.requires() for runtime deps; in build_requirements(), use self.test_requires() for test-only deps (e.g. GTest). layout() sets source/build layout; generate() emits CMakeToolchain for CMake. build() runs CMake(self).configure().build(). Prefer conanfile.py when you need options or conditional dependencies.

from conan import ConanFile
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout

class MyProjectConan(ConanFile):
    name = "myproject"
    version = "1.0.0"

    settings = "os", "compiler", "build_type", "arch"

    def requirements(self):
        self.requires("fmt/9.1.0")
        self.requires("spdlog/1.11.0")
        self.requires("boost/1.81.0")

    def build_requirements(self):
        self.test_requires("gtest/1.12.1")

    def layout(self):
        cmake_layout(self)

    def generate(self):
        tc = CMakeToolchain(self)
        tc.generate()

    def build(self):
        cmake = CMake(self)
        cmake.configure()
        cmake.build()

Options and settings

options define per-package switches: e.g. shared for dynamic vs static, with_tests to enable test builds. Set default_options, and branch in requirements() (e.g. if self.options.with_tests:) to add test_requires conditionally. Override from the CLI: conan install . -o with_tests=False—useful to skip tests in CI.

from conan import ConanFile

class MyProjectConan(ConanFile):
    options = {
        "shared": [True, False],
        "with_tests": [True, False]
    }
    default_options = {
        "shared": False,
        "with_tests": True
    }

    def requirements(self):
        self.requires("fmt/9.1.0")

        if self.options.with_tests:
            self.test_requires("gtest/1.12.1")

Custom profiles

Profile files under [settings] pin OS, arch, compiler, standard library, build type, etc. Store them under ~/.conan2/profiles/ (e.g. gcc11) and run conan install . —profile=gcc11. [conf] can switch the CMake generator to Ninja and more—one profile can encode a team standard (e.g. Ubuntu 22.04 + GCC 11 + Release).

INI example:

# ~/.conan2/profiles/gcc11
[settings]
os=Linux
arch=x86_64
compiler=gcc
compiler.version=11
compiler.libcxx=libstdc++11
build_type=Release

[conf]
tools.cmake.cmaketoolchain:generator=Ninja

Usage:

conan install . --profile=gcc11 --build=missing

A complete Conan project

Project layout

myapp-conan/
├── CMakeLists.txt
├── conanfile.txt
├── src/
│   └── main.cpp
└── tests/
    └── test_main.cpp

conanfile.txt

INI example:

[requires]
fmt/9.1.0
spdlog/1.11.0
nlohmann_json/3.11.2
boost/1.81.0

[generators]
CMakeDeps
CMakeToolchain

[layout]
cmake_layout

CMakeLists.txt

CMake example:

cmake_minimum_required(VERSION 3.15)
project(MyAppConan LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)

# Toolchain generated by conan install
find_package(fmt REQUIRED)
find_package(spdlog REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(Boost REQUIRED COMPONENTS system)

add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE
    fmt::fmt
    spdlog::spdlog
    nlohmann_json::nlohmann_json
    Boost::system
)

src/main.cpp

#include <iostream>
#include <fmt/core.h>
#include <spdlog/spdlog.h>
#include <nlohmann/json.hpp>
#include <boost/asio.hpp>

int main() {
    spdlog::info("Conan example start");

    std::cout << fmt::format("Hello, {}!\n", "Conan");

    auto j = nlohmann::json::parse(R"({"name": "myapp", "version": 1})");
    std::cout << "JSON: " << j.dump() << "\n";

    boost::asio::io_context io;
    boost::asio::steady_timer t(io, boost::asio::chrono::seconds(1));
    t.async_wait( { spdlog::info("timer done"); });
    io.run();

    return 0;
}

Build script (build.sh)

Run the following in a terminal.

#!/bin/bash
set -e

# 1. Conan dependencies
conan install . --output-folder=build --build=missing

# 2. CMake configure & build
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake
cmake --build build

echo "Build done. Run: ./build/myapp"

vcpkg or Conan?

vcpkg vs Conan (detailed)

Mermaid example:

flowchart TB
  subgraph vcpkg[vcpkg]
    V1[Git clone install]
    V2[Manifest: vcpkg.json]
    V3[Triplet: x64-windows]
    V4[CMake auto integration]
  end
  subgraph conan[Conan]
    C1[pip install]
    C2[conanfile.txt / .py]
    C3[Profile: compiler, build_type]
    C4[Run conan install first]
  end
  V2 --> V4
  C2 --> C4 --> C4
AspectvcpkgConan
InstallGit clone + bootstrappip install conan
Library count2000+ (Microsoft ports)1500+ (Conan Center)
CMakePass toolchain; manifest autoconan install → toolchain
Versionsbuiltin-baseline, version>=Explicit (fmt/9.1.0)
Build configTriplets (x64-windows, etc.)Profiles (compiler, build_type)
Learning curveEasierMedium
FlexibilityMediumHigh (Python)
Binary cachevcpkg binary cacheConan remotes
Cross-compileTripletsProfiles

When to choose which

Prefer vcpkg:

  • CMake-centric projects
  • You want a fast start
  • Microsoft stack (Visual Studio, Azure)
  • Team is C++-only

Prefer Conan:

  • Complex dependency graphs (conditionals, options)
  • Fine-grained build control
  • Team is comfortable with Python
  • You need to publish your own packages

Package-not-found, link errors, and version conflicts

“Could not find a package configuration file”

CMake Error at CMakeLists.txt:10 (find_package):
  Could not find a package configuration file provided by "fmt"

Cause: vcpkg/Conan toolchain not passed to CMake, or conan install was not run.

Fix:

Run the following in a terminal.

# vcpkg
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake

# Conan: run conan install first
conan install . --build=missing
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake
undefined reference to `fmt::v9::format(...)'

Cause: find_package worked but target_link_libraries omits the target, or Debug/Release mismatch.

Fix:

# Always link imported targets
target_link_libraries(myapp PRIVATE fmt::fmt)

# vcpkg: triplet consistency (x64-windows vs x64-windows-static)
# Conan: profile build_type matches Debug/Release

“Version conflict” (Conan)

ERROR: Version conflict: fmt/10.0.0, fmt/9.1.0

Cause: Different recipes require different fmt versions.

Fix: Unify on one version in the Conan file, or force with overrides:

INI example:

[requires]
fmt/9.1.0
spdlog/1.11.0

[tool_requires]
# In conanfile.py: self.requires("fmt/9.1.0", override=True)

vcpkg “Baseline must be a valid git commit”

Error: builtin-baseline must be a valid git commit

Cause: Invalid date or hash in builtin-baseline.

Fix: Use a real commit from the vcpkg repo:

cd vcpkg
git log -1 --format=%H
{
  "builtin-baseline": "a1c7f64b3"
}

“Package not found” (Conan)

ERROR: Package 'xyz/1.0' not found

Cause: Recipe not on Conan Center, or typo in name/version.

Fix:

conan search xyz -r conancenter

# vcpkg port names can differ (e.g. nlohmann-json vs nlohmann_json)

“A suitable version of cmake was not found”

Cause: Conan requires a newer CMake than installed.

Fix:

[tool_requires]
cmake/3.25.0

Keeping builds reproducible

Commit the dependency files

Commit vcpkg.json, conanfile.txt, and conanfile.py so teammates and CI share the same graph.

Pin versions

Example:

// vcpkg: builtin-baseline + version>=
{
  "builtin-baseline": "a1c7f64",
  "dependencies": [{ "name": "fmt", "version>=": "9.1.0" }]
}
; Conan: explicit versions
[requires]
fmt/9.1.0

Keep dependencies minimal

Request only what you need—e.g. boost-asio and boost-system instead of all of Boost.

Cache packages in CI

Example:

# GitHub Actions (vcpkg)
- uses: actions/cache@v4
  with:
    path: ${{ env.VCPKG_ROOT }}/installed
    key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json') }}

Match local and CI environments

Use the same triplet (vcpkg) or profile (Conan). If local is x64-windows, CI should use x64-windows too.

Fix the Conan build order

# build.sh
conan install . --build=missing
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake
cmake --build build

Monorepos, Docker, binary caches, and update workflows

Monorepo with manifest mode

For multiple subprojects, use one root vcpkg.json or per-service manifests under each folder.

monorepo/
├── vcpkg.json          # shared deps
├── service-a/
│   ├── vcpkg.json
│   └── CMakeLists.txt
└── service-b/
    ├── vcpkg.json
    └── CMakeLists.txt

Pinning the environment with Docker

Example:

# Dockerfile.build
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y cmake g++ git python3-pip
RUN git clone https://github.com/Microsoft/vcpkg.git /vcpkg && \
    /vcpkg/bootstrap-vcpkg.sh
ENV VCPKG_ROOT=/vcpkg
WORKDIR /app
COPY . .
RUN cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake && \
    cmake --build build

Speeding up CI with a binary cache

vcpkg: --x-use-binary-cache or Azure Artifacts integration

vcpkg install fmt --x-binarysource=clear;default

Conan: remote cache or Artifactory

conan remote add mycache https://my-artifactory.com/conan
conan upload "*" -r mycache --all

Sharing profiles and triplets

Commit profile files so everyone uses the same compiler and flags.

conan/
├── profiles/
│   ├── linux-gcc11
│   ├── windows-msvc2022
│   └── macos-clang14
conan install . --profile=conan/profiles/linux-gcc11

A dependency update workflow

  1. Bump builtin-baseline (vcpkg) or versions (Conan)
  2. Build and test locally
  3. Confirm CI
  4. Commit

The whole setup in one project

Project layout

myproject/
├── CMakeLists.txt
├── vcpkg.json          # if using vcpkg
├── conanfile.txt       # if using Conan
├── src/
│   └── main.cpp
└── tests/
    └── test_main.cpp

vcpkg.json

Example:

{
  "name": "myproject",
  "version": "1.0.0",
  "dependencies": [
    "fmt",
    "spdlog",
    "nlohmann-json",
    "gtest"
  ]
}

conanfile.txt

INI example:

[requires]
fmt/9.1.0
spdlog/1.11.0
nlohmann_json/3.11.2

[test_requires]
gtest/1.12.1

[generators]
CMakeDeps
CMakeToolchain

[layout]
cmake_layout

CMakeLists.txt

cmake_minimum_required(VERSION 3.15)
project(MyProject)

set(CMAKE_CXX_STANDARD 17)

find_package(fmt REQUIRED)
find_package(spdlog REQUIRED)
find_package(nlohmann_json REQUIRED)

add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE
    fmt::fmt
    spdlog::spdlog
    nlohmann_json::nlohmann_json
)

if(BUILD_TESTS)
    find_package(GTest REQUIRED)
    enable_testing()

    add_executable(myapp_test tests/test_main.cpp)
    target_link_libraries(myapp_test PRIVATE GTest::gtest_main)

    add_test(NAME myapp_test COMMAND myapp_test)
endif()

Setup checklist for vcpkg or Conan

  • Install vcpkg or Conan
  • Add vcpkg.json or conanfile.txt
  • Set builtin-baseline (vcpkg) or explicit versions (Conan)
  • Pass toolchain file to CMake
  • Add every dependency to target_link_libraries
  • Use the same toolchain/profile in CI
  • Optionally configure dependency caches

vcpkg and Conan commands side by side

TaskvcpkgConan
Installvcpkg install fmtconan install .
Dependency filevcpkg.jsonconanfile.txt / conanfile.py
CMakeToolchain + auto manifestconan install → toolchain
PinningBaseline + version>=Explicit versions

Note the pinning row: a vcpkg manifest without a builtin-baseline resolves versions from whatever vcpkg checkout each machine happens to have, so two machines can build different library versions from the same vcpkg.json. Commit the baseline (and a Conan lockfile, if you use Conan) together with the dependency file.

FAQ

Q. CMake says “Could not find a package configuration file provided by fmt” even though I installed it. Why?

A. CMake does not know where the package manager put the files. With vcpkg, pass -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake; with Conan, run conan install . --build=missing first and then point CMake at the generated conan_toolchain.cmake. If find_package succeeds but linking fails with undefined reference, check that target_link_libraries names the imported target (for example fmt::fmt) and that Debug/Release settings match.

Q. Should I pick vcpkg or Conan?

A. vcpkg is the quicker start for CMake-centric projects and Visual Studio users. Conan fits better when the dependency graph has conditional options, you need fine-grained build control, or you want to publish your own packages, at the cost of a Python-based toolchain. Either way, commit the manifest (vcpkg.json or conanfile) and pin versions so local and CI builds resolve the same dependencies.

With vcpkg and Conan you declare external libraries and automate install and linking.

Next: Google Test (#18-1)

Previous: Advanced CMake (#17-1): large-project build systems