Skip to content
Dev Tools Beginner Tutorial

Set Up a Modern C++ Project with CMake, vcpkg, and clang-tidy

Wire up dependency management and static analysis from scratch so every build is clean and lint-checked.

Priya Nair
Priya Nair
AI & Developer Experience Writer · Jul 3, 2026 · 5 min read
Set Up a Modern C++ Project with CMake, vcpkg, and clang-tidy

What you'll build

A minimal but functional C++ project that pulls in a third-party library (fmt) via vcpkg's manifest mode, builds with CMake, and runs clang-tidy automatically on every compile. The same commands work on all three major platforms.

Prerequisites

Tool Minimum version Where to get it
CMake 3.21 cmake.org/download
Git any recent git-scm.com
C++ compiler C++17-capable see notes below
clang-tidy 14+ via LLVM

Platform notes:

  • macOS: xcode-select --install gives you Apple Clang. Install clang-tidy separately with brew install llvm, then add /opt/homebrew/opt/llvm/bin (Apple Silicon) or /usr/local/opt/llvm/bin (Intel) to your PATH.
  • Linux (Debian/Ubuntu): sudo apt install cmake clang clang-tidy; Fedora: sudo dnf install cmake clang clang-tools-extra.
  • Windows: Install Visual Studio 2022 with the "C++ CMake tools for Windows" and "C++ Clang tools for Windows" workload components, or install LLVM from llvm.org and CMake separately.

1. Install vcpkg

Clone it somewhere permanent on your machine. Do not put it inside your project.

git clone https://github.com/microsoft/vcpkg.git ~/vcpkg
cd ~/vcpkg
./bootstrap-vcpkg.sh        # macOS / Linux
git clone https://github.com/microsoft/vcpkg.git $HOME\vcpkg
cd $HOME\vcpkg
.\bootstrap-vcpkg.bat       # Windows PowerShell

Then expose VCPKG_ROOT so CMake can find the toolchain file. Add this to ~/.zshrc or ~/.bashrc:

export VCPKG_ROOT="$HOME/vcpkg"

On Windows, set VCPKG_ROOT as a User environment variable pointing to where you cloned vcpkg.

2. Create the project layout

myproject/
├── CMakeLists.txt
├── vcpkg.json
├── .clang-tidy
└── src/
    └── main.cpp
mkdir -p myproject/src && cd myproject

3. Write the files

vcpkg.json is the manifest. vcpkg reads it during CMake configure and builds your dependencies automatically.

{
  "name": "myproject",
  "version": "0.1.0",
  "dependencies": ["fmt"]
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.21)
project(myproject LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

find_package(fmt CONFIG REQUIRED)

add_executable(myproject src/main.cpp)
target_link_libraries(myproject PRIVATE fmt::fmt)

find_program(CLANG_TIDY_EXE NAMES clang-tidy)
if(CLANG_TIDY_EXE)
  set_target_properties(myproject PROPERTIES
    CXX_CLANG_TIDY "${CLANG_TIDY_EXE}")
endif()

The if(CLANG_TIDY_EXE) guard matters. Without it, CMake sets the variable to CLANG_TIDY_EXE-NOTFOUND when the tool is missing, and passing that string to CXX_CLANG_TIDY makes the build fail with a confusing "failed to run" error. The guard lets the build succeed without linting when clang-tidy isn't installed.

.clang-tidy controls which checks run. This is a reasonable starting set:

Checks: "clang-diagnostic-*,clang-analyzer-*,modernize-*,readability-simplify-boolean-expr"
WarningsAsErrors: "modernize-*"

src/main.cpp:

#include <fmt/core.h>

int main() {
    fmt::print("Hello, {}!\n", "world");
    return 0;
}

4. Configure and build

Pass the vcpkg toolchain file to CMake. This is what lets find_package locate vcpkg-installed libraries.

cmake -B build -S . \
  -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake"
cmake -B build -S . `
  -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake"

vcpkg downloads and compiles fmt during this step. Then build:

cmake --build build

Verify it works

Run the binary:

./build/myproject              # macOS / Linux
.\build\Debug\myproject.exe   # Windows

Expected output:

Hello, world!

During cmake --build, you should see clang-tidy running alongside the compiler. If a check fires, it prints the file, line number, and check name. modernize-* violations fail the build because of WarningsAsErrors.

Troubleshooting

Could not find a package configuration file provided by "fmt" You forgot -DCMAKE_TOOLCHAIN_FILE. Delete the build/ directory (CMake cached the bad state) and re-run configure with the flag.

clang-tidy checks aren't running during build If CMake couldn't find clang-tidy on PATH at configure time, it skips setting CXX_CLANG_TIDY and the build proceeds without linting. Check the CMake output for the resolved path. On macOS, confirm the Homebrew LLVM bin directory is in your PATH before configuring. On Linux: which clang-tidy. After fixing PATH, delete build/ and re-run configure.

vcpkg build fails or takes very long vcpkg compiles packages from source on first use for a given platform triplet. This is expected and can take several minutes. Subsequent runs hit the binary cache (~/.cache/vcpkg on Linux/macOS, %LOCALAPPDATA%\vcpkg on Windows). Make sure you have a working internet connection and a few hundred MB free.

VCPKG_ROOT is empty at configure time Environment variables set in your shell profile only apply to new shells. Open a fresh terminal after editing .zshrc or .bashrc, or run source ~/.zshrc to reload it.

Next steps

  • Add a CMakePresets.json to encode the toolchain path and build type into named presets, so you replace that long -D flag with cmake --preset default.
  • Set up vcpkg binary caching for CI to avoid rebuilding dependencies from source on every pipeline run.
  • Add a GitHub Actions workflow that configures, builds, and checks clang-tidy output so linting failures block merges.
  • Look at FetchContent for header-only or CMake-native dependencies that aren't in the vcpkg registry yet.
Priya Nair
Written by
Priya Nair · AI & Developer Experience Writer

Priya covers AI frameworks, developer productivity tooling, and the startup ecosystem across South and Southeast Asia, bringing a researcher's rigour and a practitioner's empathy to every story. She is deeply sceptical of benchmarks and asks hard questions so her readers don't have to.

Discussion 0

Join the discussion

Sign in or create an account to comment and vote.

No comments yet

Be the first to weigh in.

Related Reading