Conan 2 — C++ Package Manager
"Conan 2 reference: conanfile.py, conanfile.txt, profiles, generators, binary compatibility, and CMake integration."
TL;DR
Conan 2 is a decentralized C++ package manager with binary caching and full binary compatibility management. Use conanfile.txt for simple projects, conanfile.py for complex ones. Conan Center Index has 1500+ packages.
pip install conan
conan profile detect # auto-detect your compiler/OSQuick start with conanfile.txt
# conanfile.txt
[requires]
fmt/10.1.1
nlohmann_json/3.11.3
boost/1.83.0
[generators]
CMakeDeps
CMakeToolchain# Install dependencies — downloads or builds binaries
conan install . --output-folder=build --build=missing
# Configure CMake using the generated toolchain
cmake -B build -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake
cmake --build build# CMakeLists.txt
find_package(fmt REQUIRED)
find_package(nlohmann_json REQUIRED)
target_link_libraries(myapp PRIVATE fmt::fmt nlohmann_json::nlohmann_json)conanfile.py — for complex requirements
# conanfile.py
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout
class MyAppConan(ConanFile):
name = "my-app"
version = "1.0"
settings = "os", "compiler", "build_type", "arch"
generators = "CMakeDeps", "CMakeToolchain"
def requirements(self):
self.requires("fmt/10.1.1")
self.requires("openssl/3.1.2")
if self.settings.os == "Linux":
self.requires("libpthread-stubs/0.4")
def build_requirements(self):
self.tool_requires("cmake/3.27.1")
self.tool_requires("ninja/1.11.1")
def layout(self):
cmake_layout(self)
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()Profiles — compiler configuration
Conan uses profiles to define the compiler, stdlib, and build settings:
# ~/.conan2/profiles/default (created by: conan profile detect)
[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=20
compiler.libcxx=libstdc++11
compiler.version=13
os=LinuxMultiple profiles
# ~/.conan2/profiles/clang17
[settings]
arch=x86_64
build_type=Debug
compiler=clang
compiler.cppstd=20
compiler.libcxx=libc++
compiler.version=17
os=Linux
[buildenv]
CC=clang-17
CXX=clang++-17# Use a specific profile
conan install . --profile=clang17 --output-folder=build --build=missing
# Cross-compile: host profile + build profile
conan install . \
--profile:host=arm-linux \
--profile:build=default \
--output-folder=build --build=missingBinary management
Conan's key feature: binaries are identified by a hash of (package name, version, settings, options, dependencies). If a matching binary exists in the cache or a remote, it's downloaded. Otherwise, it's built from source.
# Check if binary exists without building
conan install . --output-folder=build
# Force build from source
conan install . --output-folder=build --build=missing # build only if no binary
conan install . --output-folder=build --build=* # build everything
# Show binary compatibility hash
conan search fmt/10.1.1 -r conancenter
# List cached packages
conan list "fmt:*"Package options
Many packages expose options (static/shared, with/without features):
# conanfile.txt
[requires]
boost/1.83.0
[options]
boost*:shared=False # static linking
boost*:without_python=True
openssl*:shared=True# conanfile.py
def requirements(self):
self.requires("boost/1.83.0")
def configure(self):
self.options["boost"].shared = False
self.options["boost"].without_filesystem = FalseConan Center Index
1500+ pre-built open source packages at conan.io/center:
# Search packages
conan search fmt -r conancenter
# Get package info
conan inspect fmt/10.1.1 -r conancenter
# Common packages
fmt/10.1.1 # {fmt} formatting library
nlohmann_json/3.11.3 # JSON for Modern C++
boost/1.83.0 # Boost libraries
openssl/3.1.2 # TLS/crypto
zlib/1.3 # Compression
sqlite3/3.43.2 # Embedded database
protobuf/4.24.4 # Protocol Buffers
grpc/1.56.2 # gRPC
catch2/3.4.0 # Unit testing
gtest/1.14.0 # Google Test
benchmark/1.8.3 # Google Benchmark
spdlog/1.12.0 # Fast logging
eigen/3.4.0 # Linear algebraPrivate Artifactory remote
For enterprise or proprietary packages:
# Add a remote
conan remote add mycompany https://artifactory.mycompany.com/artifactory/api/conan/conan-local
# Login
conan remote login mycompany myuser --password mypassword
# Upload a built package
conan upload "mylib/1.0" -r mycompany
# Install from multiple remotes (searched in order)
conan install . -r conancenter -r mycompany --output-folder=buildCI with GitHub Actions
- name: Install Conan
run: pip install conan
- name: Detect profile
run: conan profile detect
- name: Cache Conan packages
uses: actions/cache@v4
with:
path: ~/.conan2/p
key: conan-${{ hashFiles('conanfile.*') }}-${{ runner.os }}
- name: Install dependencies
run: conan install . --output-folder=build --build=missing
- name: Configure and build
run: |
cmake -B build -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake
cmake --build buildConan 1 → Conan 2 migration
Conan 2 (released 2023) has breaking changes from Conan 1:
| Conan 1 | Conan 2 | |
|---|---|---|
| Generator | cmake | CMakeDeps + CMakeToolchain |
| Profile detection | manual | conan profile detect |
conanfile.py imports | from conans import ConanFile | from conan import ConanFile |
self.copy() | available | removed — use copy() from conan.tools.files |
cpp_info.libs | direct | still works |
build_requires | method name | renamed to build_requirements |
Writing a Conan Package (conanfile.py as a recipe)
A Conan recipe is just a Python class — source() fetches the code, build() compiles it, package() installs headers and libs, and package_info() tells consumers what to link. Once you understand this lifecycle, packaging any library is mechanical.
# conanfile.py (library recipe)
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout
from conan.tools.files import copy, get
import os
class MyLibConan(ConanFile):
name = "mylib"
version = "1.0"
license = "MIT"
url = "https://github.com/myorg/mylib"
description = "A fast C++ utility library"
settings = "os", "compiler", "build_type", "arch"
options = {"shared": [True, False], "fPIC": [True, False]}
default_options = {"shared": False, "fPIC": True}
def source(self):
# Fetch from GitHub (tag or commit)
get(self, "https://github.com/myorg/mylib/archive/refs/tags/v1.0.tar.gz",
strip_root=True)
def layout(self):
cmake_layout(self)
def generate(self):
tc = CMakeToolchain(self)
tc.variables["BUILD_SHARED_LIBS"] = self.options.shared
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
# In Conan 2: use copy() from conan.tools.files, NOT self.copy()
copy(self, "*.h",
src=os.path.join(self.source_folder, "include"),
dst=os.path.join(self.package_folder, "include"))
copy(self, "*.a",
src=self.build_folder,
dst=os.path.join(self.package_folder, "lib"),
keep_path=False)
copy(self, "*.so*",
src=self.build_folder,
dst=os.path.join(self.package_folder, "lib"),
keep_path=False)
def package_info(self):
# Tells consumers: target_link_libraries(myapp mylib::mylib)
self.cpp_info.libs = ["mylib"]
self.cpp_info.includedirs = ["include"]Every recipe needs a test_package/ directory so Conan can verify the build before uploading. Keep the test small — just #include the library and call one function.
# test_package/conanfile.py
from conan import ConanFile
from conan.tools.cmake import CMake, cmake_layout
class TestMyLib(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "CMakeDeps", "CMakeToolchain"
def requirements(self):
self.requires(self.tested_reference_str)
def layout(self):
cmake_layout(self)
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def test(self):
self.run(os.path.join(self.cpp.build.bindir, "test_mylib"))# Build and test the package locally
conan create . --version=1.0
# Build with a specific profile
conan create . --version=1.0 --profile=clang17 --build=missingConan Lockfiles
Lockfiles pin every resolved version and binary hash so that conan install on a CI machine two months from now uses exactly the same dependency graph as your dev machine today. Check conan.lock into source control the same way you would package-lock.json.
# Generate a lockfile from the current resolution
conan lock create .
# Install using the locked graph — no resolution, fully reproducible
conan install . --lockfile=conan.lock --output-folder=build --build=missing
# Update a single package while keeping the rest pinned
conan lock update conan.lock --requires="fmt/11.0.0"
# Inspect the locked graph
conan graph info . --lockfile=conan.lockPartial lockfiles are useful when you only want to pin some packages. Use --lockfile-partial to generate a lockfile that only records the packages you specify and leaves others floating.
# Create a partial lockfile (pins fmt and openssl, leaves others free)
conan lock create . --lockfile-partial \
--requires="fmt/10.1.1" --requires="openssl/3.1.2" \
--lockfile-out=partial.lock
# Install using the partial lockfile
conan install . --lockfile=partial.lock --output-folder=build --build=missingConan Center Index Contribution
Conan Center Index (CCI) is the official, community-maintained recipe repository at github.com/conan-io/conan-center-index. Every package in conancenter comes from a PR there. Contributing your library means anyone with Conan can install it with a single line in conanfile.txt.
The review process works like this: you fork the repo, add your recipe under recipes/<library-name>/, open a PR, and CCI's bot runs hook checks, linting, and test builds across Linux/macOS/Windows. Reviewers ask for changes before merging.
Key checklist for a successful CCI PR:
- Recipe lives at
recipes/<libname>/all/conanfile.py(or versioned sub-dirs for breaking changes) test_package/is mandatory and must build a real executablesource()must download from an official release URL (no Git clones)- No vendored dependencies — each dep must be a separate Conan requirement
- Options follow CCI conventions:
shared,fPIC,header_only conan-hooksCI passes:conan-center-index-hookslint checks naming, license, patches- Patch files go in
recipes/<libname>/all/patches/and must be upstreamed or minimal - The recipe must build on all three platforms without skipping
# Test your recipe locally against CCI hooks before opening a PR
pip install conan-center-index-hooks
conan create recipes/mylib/all --version=1.0 -pr:b=default -pr:h=defaultCross-Compilation with Conan
Conan separates the machine that builds the code (build profile) from the machine that runs it (host profile) — pass both on the command line and Conan handles the rest, including selecting the right binary packages for each context.
Define a host profile for your target architecture. This example targets a 64-bit ARM Linux board using a GCC 12 cross-compiler installed on an x86_64 Ubuntu host.
# ~/.conan2/profiles/armlinux
[settings]
arch=armv8
build_type=Release
compiler=gcc
compiler.cppstd=20
compiler.libcxx=libstdc++11
compiler.version=12
os=Linux
[buildenv]
CC=aarch64-linux-gnu-gcc-12
CXX=aarch64-linux-gnu-g++-12
AR=aarch64-linux-gnu-ar
STRIP=aarch64-linux-gnu-strip
[conf]
tools.gnu:cross_building=True
tools.build:sysroot=/sysroot/aarch64# Install deps for the ARM target; build tools stay native
conan install . \
--profile:host=armlinux \
--profile:build=default \
--output-folder=build \
--build=missing
# CMake picks up the cross-toolchain automatically
cmake -B build \
-DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake \
-DCMAKE_BUILD_TYPE=Release
cmake --build buildtool_requires (e.g., cmake, ninja) are installed for the build profile (your host machine). Regular requires are installed for the host profile (the ARM target). Conan keeps the two graphs completely separate.
Using Conan in Meson Projects
Conan and Meson integrate through the MesonToolchain generator, which writes a native-file (or cross-file for cross-compilation) that Meson reads to find include paths, library paths, and compiler flags provided by Conan packages.
Use the MesonToolchain generator instead of CMakeDeps/CMakeToolchain in your recipe or conanfile.txt:
# conanfile.txt
[requires]
fmt/10.1.1
nlohmann_json/3.11.3
[generators]
MesonToolchain
PkgConfigDeps# Generate the Meson native file
conan install . -g MesonToolchain --output-folder=build --build=missing
# Pass the native file to Meson setup
meson setup build --native-file build/conan_meson_native.ini
# Build as normal
ninja -C buildFor cross-compilation, Conan generates a conan_meson_cross.ini file instead. Pass it with --cross-file to meson setup.
# Cross-compile with Meson + Conan
conan install . \
--profile:host=armlinux \
--profile:build=default \
-g MesonToolchain \
--output-folder=build \
--build=missing
meson setup build --cross-file build/conan_meson_cross.ini
ninja -C buildIn a conanfile.py-based project, use MesonToolchain inside generate() instead of CMakeToolchain, and replace CMake(self) with Meson(self) in build():
from conan import ConanFile
from conan.tools.meson import MesonToolchain, Meson
from conan.tools.layout import basic_layout
class MyAppConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "PkgConfigDeps"
def layout(self):
basic_layout(self, src_folder="src")
def generate(self):
tc = MesonToolchain(self)
tc.generate()
def build(self):
meson = Meson(self)
meson.configure()
meson.build()See Also
- vcpkg — Microsoft's alternative package manager with a curated port tree and CMake integration
- CPM — lightweight CMake-native dependency manager based on FetchContent
- CMake — the build system Conan integrates with most tightly
- Cross-Compilation — deeper coverage of cross-toolchain setup independent of the package manager