Best Practices and Workflows
About 1540 wordsAbout 5 min
2025-12-22
Following industry best practices with Conan will make your C++ development more reliable, faster, and easier to maintain. Think of these as professional habits that separate amateur projects from enterprise-grade software.
Project Structure Best Practices
Recommended Directory Structure
my-awesome-project/
├── conanfile.py # Main Conan configuration
├── conanfile.txt # Alternative for simple projects
├── CMakeLists.txt # Build system configuration
├── src/ # Source code
│ ├── main.cpp
│ ├── lib/
│ │ ├── CMakeLists.txt
│ │ └── mylib/
│ │ ├── header.h
│ │ └── implementation.cpp
│ └── app/
│ ├── CMakeLists.txt
│ └── myapp.cpp
├── include/ # Public headers (if library)
│ └── myproject/
│ └── public_header.h
├── tests/ # Test files
│ ├── conanfile.py # Test-specific dependencies
│ ├── test_main.cpp
│ └── CMakeLists.txt
├── docs/ # Documentation
│ └── README.md
├── profiles/ # Custom profiles (optional)
│ ├── development
│ ├── release
│ └── cross-arm
├── .github/ # CI/CD workflows
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── LICENSE
└── README.mdFile Naming Conventions
# Good naming patterns
# conanfile.py - Main project configuration
class MyAwesomeProjectConan(ConanFile):
name = "myawesomeproject" # Lowercase with underscores
version = "1.0.0" # Semantic versioning
# NOT:
# class MyAwesomeProjectConan(ConanFile):
# name = "MyAwesomeProject" # Avoid spaces and capitals
# Version examples
# Good: "1.0.0", "2.1.3", "1.0.0-beta"
# Bad: "v1.0", "final", "latest"Conanfile.py Best Practices
Keep It Simple and Maintainable
Good: Simple and Clear
from conan import ConanFile
from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps, cmake_layout
class MyProjectConan(ConanFile):
name = "myproject"
version = "1.0.0"
license = "MIT"
author = "Your Name <your.email@company.com>"
description = "A great C++ project"
topics = ("cpp", "networking", "async")
settings = "os", "compiler", "build_type", "arch"
options = {
"shared": [True, False],
"fPIC": [True, False],
"enable_logging": [True, False],
}
default_options = {
"shared": False,
"fPIC": True,
"enable_logging": True,
}
requires = ("fmt/10.0.0",)
def configure(self):
if self.options.shared:
self.options.fPIC = False
def layout(self):
cmake_layout(self)
def generate(self):
tc = CMakeToolchain(self)
tc.variables["MYPROJECT_VERSION"] = self.version
tc.generate()
deps = CMakeDeps(self)
deps.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package_info(self):
self.cpp_info.libs = ["myproject"]Bad: Over-Complex
# Too many options, complex logic
class MyProjectConan(ConanFile):
options = {
"shared": [True, False],
"static": [True, False], # Redundant with shared
"debug": [True, False], # Build type handles this
"optimization_level": [0, 1, 2, 3], # Should be in settings
"compiler_vendor": ["gcc", "clang", "msvc"], # Should be in settings
"custom_flag_1": [True, False], # Unnecessary complexity
"custom_flag_2": [True, False],
"custom_flag_3": [True, False],
# ... 20 more options
}Dependency Management Best Practices
Minimal Dependencies
# Good: Only what you really need
class SimpleProjectConan(ConanFile):
requires = ("fmt/10.0.0",) # One dependency
# Bad: Too many dependencies
class ComplexProjectConan(ConanFile):
requires = ( # Do you really need all of these?
"boost/1.81.0", # Heavy dependency
"eigen/3.4.0", # If you use only basic linear algebra
"opencv/4.8.0", # If you only need basic image I/O
"qt/6.4.0", # If you're not building a GUI
"sqlite/3.42.0", # If you only need simple data storage
# ... and 20 more
)Version Management
# Good: Reasonable version ranges
class WellVersionedConan(ConanFile):
requires = (
"fmt/[>=10.0.0 <11.0.0]", # Allow bug fixes
"openssl/3.*", # Allow new features
"spdlog/1.11.0", # Specific version (OK for stability)
)
# Bad: Too restrictive or too broad
class PoorlyVersionedConan(ConanFile):
requires = (
"fmt/10.0.0", # Too restrictive - breaks on bug fixes
"openssl/>0.0.0", # Too broad - could break compatibility
)Dependency Documentation
class WellDocumentedConan(ConanFile):
"""
MyProject - A high-performance networking library
This package provides asynchronous network communication with:
- TCP/UDP socket support
- SSL/TLS encryption
- HTTP/HTTPS protocols
- Cross-platform compatibility
Dependencies:
- fmt: Text formatting and logging (required)
- openssl: SSL/TLS encryption (required)
- spdlog: High-performance logging (optional, see enable_logging)
Example:
from conan import ConanFile
class MyProjectConan(ConanFile):
requires = ("myproject/1.0.0",)
# Use in CMake:
find_package(myproject REQUIRED)
target_link_libraries(myapp PRIVATE myproject::myproject)
"""
# ... rest of the configurationProfile Management Best Practices
Organized Profile Naming
# Good: Descriptive and organized
conan profile new dev-gcc11-debug
conan profile new rel-gcc11-release-optimized
conan profile new rel-msvc2022-release
conan profile new cross-arm-debug
conan profile new ci-linux-gcc11-release
# Bad: Unclear names
conan profile new profile1
conan profile new debug
conan profile new linux
conan profile new new-profileProfile Documentation
# dev-gcc11-debug.profile
# Development profile for GCC 11, Debug builds
# Usage: conan install . --profile=dev-gcc11-debug
# Purpose: Local development with debug symbols and sanitizers
[settings]
os=Linux
arch=x86_64
compiler=gcc
compiler.version=11
compiler.libcxx=libstdc++11
compiler.cppstd=17
build_type=Debug
[options]
*:shared=False # Static linking for easier debugging
[tool_requires]
cmake/3.27.0
[env]
CFLAGS=-g -O0 -fsanitize=address -fsanitize=leak
CXXFLAGS=-g -O0 -fsanitize=address -fsanitize=leak -DDEBUG
[conf]
# Keep build directories for debugging
tools.cmake.cmake_layout:build_folder_vars=['settings']Team Profile Sharing
# Store profiles in version control
mkdir profiles
cp ~/.conan2/profiles/* profiles/
git add profiles/
git commit -m "Add Conan profiles"
# Team members can import
cp profiles/* ~/.conan2/profiles/
conan profile list # See shared profilesBuild Optimization Best Practices
Fast Builds
Use sstate Cache
# Configure sstate cache location
export CONAN_USER_HOME=/path/to/fast/ssd/.conan2
# In profile:
[conf]
# Share sstate cache across projects
tools.cmake.cmake_layout:build_folder_vars=['settings']Parallel Builds
# In profile:
[env]
MAKEFLAGS=-j8
CMAKE_BUILD_PARALLEL_LEVEL=8
[conf]
# Use all CPU cores
tools.cmake.cmaketools:parallel=TrueIncremental Builds
# Only rebuild what changed
conan install . --build=missing # Don't rebuild existing packages
conan install . --build=outdated # Rebuild outdated packages onlyBinary Cache Management
# Set up binary cache in fast storage
export CONAN_USER_HOME=/fast/ssd/.conan2
# Use binary cache for multiple projects
[conf]
# Cache location
tools.cmake.cmake_layout:cache_folder=/fast/ssd/conan-cache
# Build policy
tools.cmake.cmake_layout:build_folder_vars=['settings']CI/CD Integration Best Practices
Jenkins Pipeline Example
// Jenkinsfile
pipeline {
agent any
environment {
CONAN_USER_HOME = "${WORKSPACE}/.conan2"
PYTHONUNBUFFERED = "1"
}
stages {
stage("Setup") {
steps {
script {
// Install Conan
sh "pip install conan"
// Detect/create Conan profile
sh "conan profile detect --force"
}
}
}
stage("Install Dependencies") {
steps {
script {
// Install dependencies for different configurations
def configs = [
[name: "Linux GCC11 Debug", profile: "linux-gcc11-debug"],
[name: "Linux GCC11 Release", profile: "linux-gcc11-release"],
[name: "Linux Clang14 Debug", profile: "linux-clang14-debug"]
]
parallel configs.collect { config ->
"${config.name}": {
sh "conan install . --profile=${config.profile} --build=missing"
}
}
}
}
}
stage("Build") {
steps {
script {
sh "conan build ."
}
}
}
stage("Test") {
steps {
script {
sh "cd build/Release && ./myproject_tests"
}
}
}
stage("Package") {
steps {
script {
// Create and upload package
sh "conan create . myproject/1.0.0@user/stable"
sh "conan upload myproject/1.0.0@user/stable --remote=mycompany-remote --all"
}
}
}
}
post {
always {
// Clean up build artifacts
sh "rm -rf build/"
}
success {
echo "Pipeline completed successfully!"
}
failure {
echo "Pipeline failed. Check logs for details."
}
}
}Docker-Based CI
# Dockerfile.ci
FROM ubuntu:22.04
# Install build tools
RUN apt-get update && apt-get install -y \
build-essential \
cmake \
git \
python3 \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
# Install Conan
RUN pip3 install conan
# Set up working directory
WORKDIR /workspace
# Default command
CMD ["bash"]# docker-compose.ci.yml
version: "3.8"
services:
ci:
build:
context: .
dockerfile: Dockerfile.ci
volumes:
- .:/workspace
environment:
- CONAN_USER_HOME=/root/.conan2Security Best Practices
Dependency Security
Regular Dependency Updates
# Check for outdated dependencies
conan install . --build=outdated
# Update specific packages
conan install . --build=outdated --requires=openssl/3.0.8Vulnerability Scanning
# Check for known vulnerabilities in dependencies
class SecureProjectConan(ConanFile):
# Only use well-maintained packages
requires = (
"openssl/3.0.8", # Well-maintained, frequent security updates
"fmt/10.0.0", # Active development and security fixes
)
# Avoid packages with known vulnerabilities
# NOT: "old-package/1.0.0" # If it has known security issuesSupply Chain Security
# Verify package sources
class SecureProjectConan(ConanFile):
def source(self):
# Use HTTPS URLs
get(self, "https://github.com/fmtlib/fmt/archive/refs/tags/10.0.0.tar.gz")
# Verify checksums if available
# download(self, "https://example.com/file.tar.gz", sha256="expected_checksum")Authentication Best Practices
# Use tokens instead of passwords
conan remote auth mycompany --username=$CONAN_USERNAME --password=$CONAN_TOKEN
# Environment-based authentication
export CONAN_USERNAME=your_username
export CONAN_PASSWORD=your_token_here
# Don't hardcode credentials
# BAD: conan remote auth myremote --username=admin --password=secret123Testing Best Practices
Package Testing
class MyProjectConan(ConanFile):
# Enable testing
test_type = "explicit"
def build_requirements(self):
# Testing framework
self.test_requires("gtest/1.13.0")
def build(self):
# Build main library
super().build()
# Build and run tests
self.run("cd tests && make test")
# Or use test_package
cmake = CMake(self)
cmake.configure(source_folder="test_package")
cmake.build()
cmake.test() # Run ctestContinuous Testing
# Test packages before publishing
conan create . myproject/1.0.0@user/testing
conan test test_package myproject/1.0.0@user/testing
# Test with different configurations
conan create . myproject/1.0.0@user/testing --profile=dev-debug
conan create . myproject/1.0.0@user/testing --profile=rel-releaseDocumentation Best Practices
README Structure
# MyProject
A high-performance C++ networking library.
## Features
- Asynchronous I/O
- SSL/TLS support
- Cross-platform compatibility
- Zero-copy operations
## Quick Start
### Using Conan
1. Add dependency to your `conanfile.py`:
```python
class YourProjectConan(ConanFile):
requires = ("myproject/1.0.0",)
```- Use in CMake:
find_package(myproject REQUIRED)
target_link_libraries(your_app PRIVATE myproject::myproject)Installation
Conan (Recommended)
conan install . --build=missingBuild from Source
git clone https://github.com/yourorg/myproject.git
cd myproject
conan create . myproject/1.0.0@user/stableDocumentation
- API Reference
- Examples
- Contributing
Requirements
- C++17 or later
- CMake 3.15 or later
- Conan 2.x
License
MIT License - see LICENSE file.
## Version Control Best Practices
### .gitignore Configuration
```gitignore
# Conan build artifacts
build/
generated/
conanbuildinfo.cmake
conanbuildinfo.txt
# Conan cache (can be shared)
# ~/.conan2/ # Usually in .gitignore
# IDE files
.vscode/
.idea/
*.swp
*.swo
*~
# OS files
.DS_Store
Thumbs.db
# Package files
*.tar.gz
*.zipVersion Management
# Dynamic version from git
class MyProjectConan(ConanFile):
version = "1.0.0" # Or derive from git tags
def source(self):
# Get version from git
self.output.info(f"Building version: {self.version}")Performance Best Practices
Build Time Optimization
# Use pre-built binaries when possible
conan install . --build=missing # Don't rebuild existing packages
# Parallel operations
conan install . --build=missing -j8
# Fast storage for build cache
export CONAN_USER_HOME=/fast/ssd/.conan2Memory Optimization
# Limit parallel builds
[env]
MAKEFLAGS=-j4 # Don't use all cores
[conf]
# Limit concurrent downloads
tools.cmake.cmake_layout:parallel_limit=4Key Points: Best Practices
- Keep it simple - Don't over-complicate your conanfile.py
- Document everything - Clear documentation saves time later
- Use version ranges - Allow flexibility while maintaining stability
- Organize profiles - Descriptive names and documentation
- Security first - Regular updates and secure authentication
- Test thoroughly - Test with multiple configurations and platforms
- CI/CD integration - Automate testing and building
- Performance matters - Optimize for faster builds and development