CMakePresets.json으로 빌드 설정 통일하기: 멀티 플랫폼 프리셋, vcpkg 바이너리 캐시, CI
들어가며: “cmake 명령어가 팀원마다 달라요”
cmake 명령이 사람마다 다를 때 생기는 일
CMake로 빌드하다 보면 이런 상황을 자주 마주합니다:
"로컬에서는 되는데 CI에서만 빌드가 실패해요."
"Debug로 빌드하려면 -DCMAKE_BUILD_TYPE=Debug를 매번 치기 귀찮아요."
"Windows는 MSVC, Linux는 GCC, macOS는 Clang인데 설정이 다 달라요."
"vcpkg 쓰는 사람은 CMAKE_TOOLCHAIN_FILE을, Conan 쓰는 사람은 다른 경로를 지정해요."
"팀원 A는 build/, B는 out/, C는 cmake-build-debug/를 쓰는데 정리가 안 돼요."
"IDE(CLion, VS Code)에서 preset을 선택하는 게 불편해요."
"Ninja vs Makefile vs Visual Studio 생성기가 섞여 있습니다."
CMake Presets로 해결:
| 문제 | Presets 해결 |
|---|---|
| 팀마다 다른 cmake 옵션 | CMakePresets.json에 설정 통일, Git으로 공유 |
| Debug/Release 전환 | configurePresets에 debug, release 분리 |
| 멀티 컴파일러 | gcc, clang, msvc preset 각각 정의 |
| vcpkg/Conan | toolchainFile로 자동 지정 |
| CI 빌드 | cmake --preset ci-release 한 줄로 재현 |
| IDE 연동 | CLion, VS Code, Visual Studio가 preset 자동 인식 |
팀 전체가 같은 configure/build 옵션을 쓰게 만든다는 점에서, Rust의 Cargo 워크플로·npm package.json·lock·Go 모듈·빌드 캐시·Python 가상환경·락과 같은 “재현 가능한 빌드” 문제를 나란히 떠올리면 설계 의도가 잘 맞습니다. 큰 그림은 CMake 가이드·빌드 시스템 비교와 함께 보세요.
요구 환경: CMake 3.19+ (configure), 3.20+ (build/test), 3.23+ 권장
프리셋 파일의 version마다 쓸 수 있는 기능이 다릅니다. 자주 쓰는 것만 추리면 다음과 같습니다.
| version | 필요한 CMake | 추가된 주요 기능 |
|---|---|---|
| 2 | 3.20 | build·test 프리셋 |
| 3 | 3.21 | condition, toolchainFile, installDir, generator 생략 가능 |
| 4 | 3.23 | include로 다른 프리셋 파일 합치기 |
| 6 | 3.25 | workflowPresets, packagePresets |
version을 필요 이상으로 높이면 오래된 CMake를 쓰는 팀원이나 CI 이미지에서 “Unrecognized version” 오류로 파일 전체를 읽지 못합니다. 저는 팀의 최저 CMake 버전을 먼저 정하고 그 버전이 지원하는 값으로 고정합니다. 참고로 CMake 4.0(2025년 3월)부터는 cmake_minimum_required(VERSION 3.5) 미만을 선언한 서드파티 프로젝트가 configure 단계에서 실패하는데, 프리셋의 cacheVariables에 "CMAKE_POLICY_VERSION_MINIMUM": "3.5"를 넣어 임시로 넘길 수 있습니다.
CMakePresets.json의 기본 구조와 상속을 익히고, Debug/Release, GCC/Clang/MSVC, vcpkg·Conan 설정을 프리셋으로 묶어 CI와 IDE에서 같은 설정을 쓰는 방법을 다룹니다.
설정 불일치·CI 실패·도구 혼재: Presets가 필요한 상황
팀 빌드 설정 불일치
개발자 A: cmake -B build -DCMAKE_BUILD_TYPE=Release -G Ninja
개발자 B: cmake ...-DCMAKE_BUILD_TYPE=Debug
개발자 C: cmake -S . -B build -DCMAKE_CXX_COMPILER=clang++
결과: “내 로컬에서는 되는데요” — CI나 다른 환경에서 재현 불가.
해결: CMakePresets.json에 default, debug, release preset을 정의하며, 모두 cmake --preset release로 통일.
CI에서 빌드 실패
로컬: Ubuntu 22.04, GCC 12, Ninja → 성공
CI: Ubuntu 22.04, 기본 GCC, Makefile → "undefined reference"
원인: Generator, 컴파일러, 빌드 타입이 CI와 로컬에서 다름.
해결: ci-debug, ci-release preset을 만들어 CI 스크립트에서 cmake --preset ci-release 사용.
vcpkg/Conan 사용자 혼재
vcpkg 사용자: -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
Conan 사용자: -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake
원인: 패키지 매니저마다 toolchain 경로가 다름.
해결: vcpkg-default, conan-default preset을 각각 정의. 사용자는 --preset vcpkg-default만 지정.
멀티 플랫폼 개발
Windows: Visual Studio 2022, x64
Linux: GCC 12, Ninja
macOS: Clang (Xcode), Ninja
해결: 플랫폼별 preset 또는 condition으로 OS에 따라 자동 선택.
Sanitizer·코드 커버리지 빌드
"ASan 빌드하려면 -DUSE_ASAN=ON을 매번 넣습니다."
"코드 커버리지 빌드 설정이 복잡해요."
해결: asan, tsan, coverage preset으로 한 번에 전환.
의존성 경로 변경
상황: vcpkg를 /opt/vcpkg에서 C:\vcpkg로 옮김
문제: 팀원마다 VCPKG_ROOT 경로가 다름
결과: "Could not find toolchain file" 에러, README에 경로 적어도 누락 발생
해결: CMakeUserPresets.json에 개인 경로만 두며, CMakePresets.json은 $env{VCPKG_ROOT}로 환경 변수에 위임. .env.example에 VCPKG_ROOT= 예시만 문서화.
IDE 프로필 불일치
상황: CLion은 cmake-build-debug/, VS Code는 build/, Visual Studio는 out/ 사용
문제: Git에 build 결과가 실수로 커밋되거나, .gitignore가 복잡해짐
결과: 빌드 산출물 경로가 제각각이라 정리·배포 스크립트 작성 어려움
해결: Preset의 binaryDir를 ${sourceDir}/build/${presetName}로 통일. 모든 IDE가 동일 preset을 쓰면 build/ 하위만 관리하면 됨.
flowchart TB
subgraph Problems[문제]
P1[팀 설정 불일치]
P2[CI 빌드 실패]
P3[vcpkg/Conan 혼재]
P4[멀티 플랫폼]
end
subgraph Solutions[CMake Presets 해결]
S1[CMakePresets.json]
S2[ci-release preset]
S3[toolchainFile preset]
S4[플랫폼별 preset]
end
P1 --> S1
P2 --> S2
P3 --> S3
P4 --> S4
CMake Presets 기본 구조
파일 위치와 역할
| 파일 | 용도 | Git |
|---|---|---|
CMakePresets.json | 프로젝트 공통 설정 | ✅ 커밋 |
CMakeUserPresets.json | 개발자 개인 설정 | ❌ .gitignore |
CMakeUserPresets.json이 있으면 CMakePresets.json을 암시적으로 include합니다. 개인용 경로·컴파일러 등은 User 쪽에 두세요.
최소 구성 예제
{
"version": 3,
"configurePresets": [
{
"name": "default",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/default",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_CXX_STANDARD": "17"
}
}
],
"buildPresets": [
{
"name": "default",
"configurePreset": "default"
}
]
}
사용:
cmake --preset default
cmake --build build/default
또는 build preset 사용:
cmake --preset default
cmake --build --preset default
상속 (inherits)
공통 설정을 base preset에 두며, 다른 preset이 상속합니다.
{
"version": 3,
"configurePresets": [
{
"name": "base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_CXX_STANDARD": "17",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
},
{
"name": "debug",
"inherits": "base",
"displayName": "Debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "release",
"inherits": "base",
"displayName": "Release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
}
],
"buildPresets": [
{ "name": "debug", "configurePreset": "debug" },
{ "name": "release", "configurePreset": "release" }
]
}
hidden: true: 직접 사용 불가, 상속용 base만inherits: 문자열 또는 배열["base", "other"]$presetName: preset 이름 매크로 (binaryDir 등에서 사용)
매크로 확장
| 매크로 | 설명 |
|---|---|
${sourceDir} | CMakeLists.txt가 있는 디렉터리 |
${presetName} | 현재 preset 이름 |
${hostSystemName} | Windows, Linux, Darwin 등 |
$env{VAR} | 환경 변수 VAR |
$penv{VAR} | 부모 환경의 VAR (prepend/append용) |
빌드 타입·컴파일러·Sanitizer·테스트 Preset 예제
Debug / Release / RelWithDebInfo
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 20,
"patch": 0
},
"configurePresets": [
{
"name": "base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_CXX_STANDARD": "17",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
},
{
"name": "debug",
"inherits": "base",
"displayName": "Debug (디버그 심볼)",
"description": "디버깅용 빌드",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "release",
"inherits": "base",
"displayName": "Release (최적화)",
"description": "배포용 빌드",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "relwithdebinfo",
"inherits": "base",
"displayName": "RelWithDebInfo",
"description": "최적화 + 디버그 심볼",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
}
}
],
"buildPresets": [
{ "name": "debug", "configurePreset": "debug" },
{ "name": "release", "configurePreset": "release" },
{ "name": "relwithdebinfo", "configurePreset": "relwithdebinfo" }
]
}
GCC / Clang / MSVC 멀티 컴파일러
{
"version": 3,
"configurePresets": [
{
"name": "base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_CXX_STANDARD": "17",
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "gcc",
"inherits": "base",
"displayName": "GCC",
"cacheVariables": {
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++"
}
},
{
"name": "clang",
"inherits": "base",
"displayName": "Clang",
"cacheVariables": {
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "msvc",
"inherits": "base",
"displayName": "MSVC",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"generator": "Ninja Multi-Config",
"architecture": {
"value": "x64",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_C_COMPILER": "cl",
"CMAKE_CXX_COMPILER": "cl"
}
}
],
"buildPresets": [
{ "name": "gcc", "configurePreset": "gcc" },
{ "name": "clang", "configurePreset": "clang" },
{ "name": "msvc", "configurePreset": "msvc" }
]
}
주의: Ninja Multi-Config는 하나의 빌드 디렉터리에 Debug/Release를 함께 두는 멀티 설정 생성기라 CMAKE_BUILD_TYPE을 쓰지 않고, 빌드할 때 --config(또는 build 프리셋의 configuration)로 고릅니다. 단일 설정 생성기(Ninja, Unix Makefiles)에서는 CMAKE_BUILD_TYPE이 configure 시점에 고정됩니다.
MSVC를 Ninja로 쓸 때 가장 많이 막히는 곳이 architecture입니다. "strategy": "set"은 Visual Studio 생성기에 -A x64를 넘기는 옵션이라 Ninja에서는 “Generator Ninja does not support platform specification” 오류가 납니다. Ninja에서는 "strategy": "external"로 두고, 실제 아키텍처는 어떤 개발자 명령 프롬프트(vcvars)에서 실행했는지로 정해집니다. Visual Studio와 VS Code CMake Tools는 external 값을 보고 알맞은 vcvars 환경을 자동으로 잡아 주지만, 일반 PowerShell에서 cmake --preset msvc를 치면 cl.exe를 찾지 못합니다. 명령줄에서는 “x64 Native Tools Command Prompt”에서 실행하거나, CI에서는 ilammy/msvc-dev-cmd 같은 액션으로 vcvars를 먼저 불러옵니다.
Sanitizer (ASan, TSan, UBSan)
{
"version": 3,
"configurePresets": [
{
"name": "base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_CXX_STANDARD": "17",
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "asan",
"inherits": "base",
"displayName": "AddressSanitizer",
"cacheVariables": {
"USE_ASAN": "ON",
"USE_UBSAN": "ON"
}
},
{
"name": "tsan",
"inherits": "base",
"displayName": "ThreadSanitizer",
"cacheVariables": {
"USE_TSAN": "ON"
}
}
],
"buildPresets": [
{ "name": "asan", "configurePreset": "asan" },
{ "name": "tsan", "configurePreset": "tsan" }
]
}
CMakeLists.txt에서 USE_ASAN, USE_TSAN 등으로 플래그를 분기합니다.
코드 커버리지 (gcov/lcov)
{
"name": "coverage",
"inherits": "base",
"displayName": "Coverage",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_CXX_FLAGS": "--coverage",
"CMAKE_EXE_LINKER_FLAGS": "--coverage"
}
}
condition 상세 (플랫폼·환경별 분기)
condition으로 preset을 특정 OS·환경에서만 활성화할 수 있습니다. CMake 3.21+에서 지원합니다.
| type | 설명 | 용도 |
|---|---|---|
equals | lhs == rhs | OS, 아키텍처 비교 |
notEquals | lhs != rhs | 환경 변수 미설정 시 제외 |
inList | string이 list에 포함 | 여러 OS 지원 (Linux, Darwin) |
notInList | string이 list에 미포함 | 특정 OS 제외 |
matches | string이 regex와 매칭 | 버전·경로 패턴 |
notMatches | string이 regex와 불일치 | 예외 패턴 |
allOf | 모든 하위 condition이 true | 복합 조건 (AND) |
anyOf | 하나라도 true | 복합 조건 (OR) |
not | 조건 반전 | 부정 |
예시: equals (Windows 전용), inList (Linux·macOS), allOf (복합)
{
"name": "msvc-release",
"condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" }
}
{
"name": "unix-release",
"condition": {
"type": "inList",
"string": "${hostSystemName}",
"list": ["Linux", "Darwin"]
}
}
{
"name": "ci-release",
"condition": {
"type": "allOf",
"conditions": [
{ "type": "equals", "lhs": "${hostSystemName}", "rhs": "Linux" },
{ "type": "notEquals", "lhs": "$env{CI}", "rhs": "" }
]
}
}
buildPresets 예제 (targets, jobs, verbose)
{
"buildPresets": [
{
"name": "release",
"configurePreset": "release",
"jobs": 8,
"configuration": "Release"
},
{
"name": "release-single",
"configurePreset": "release",
"targets": ["myapp"],
"jobs": 1
},
{
"name": "release-verbose",
"configurePreset": "release",
"verbose": true
}
]
}
jobs(-j),targets(특정 타깃),configuration(멀티 설정),verbose(상세 로그).
testPresets 예제 (filter, output, execution)
{
"testPresets": [
{
"name": "default",
"configurePreset": "release",
"output": {
"outputOnFailure": true,
"verbosity": "verbose"
},
"execution": {
"noTestsAction": "error",
"stopOnFailure": true,
"jobs": 4
}
},
{
"name": "unit-only",
"configurePreset": "release",
"filter": { "include": { "label": "unit" } },
"output": { "outputOnFailure": true }
},
{
"name": "ci-test",
"configurePreset": "ci-release",
"output": {
"outputOnFailure": true,
"outputJUnitFile": "${sourceDir}/test-results.xml"
},
"execution": { "noTestsAction": "error", "stopOnFailure": true }
}
]
}
ctest --preset default
ctest --preset unit-only
ctest --preset ci-test
filter.include.label:set_tests_properties(..., LABELS "unit")와 매칭.output.outputJUnitFile: CI JUnit XML.
vcpkg·Conan 연동
vcpkg 연동
{
"version": 3,
"configurePresets": [
{
"name": "base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_CXX_STANDARD": "17",
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "vcpkg-default",
"inherits": "base",
"displayName": "vcpkg (Release)",
"toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
"cacheVariables": {
"VCPKG_TARGET_TRIPLET": "$env{VCPKG_DEFAULT_TRIPLET}"
}
},
{
"name": "vcpkg-debug",
"inherits": "vcpkg-default",
"displayName": "vcpkg (Debug)",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"VCPKG_TARGET_TRIPLET": "x64-windows-static"
}
}
],
"buildPresets": [
{ "name": "vcpkg-default", "configurePreset": "vcpkg-default" },
{ "name": "vcpkg-debug", "configurePreset": "vcpkg-debug" }
]
}
사전 설정:
# 환경 변수 설정 (쉘 또는 .env)
export VCPKG_ROOT=/path/to/vcpkg
export VCPKG_DEFAULT_TRIPLET=x64-linux # 또는 x64-windows, arm64-osx 등
Conan 연동
Conan은 conan install 후 conan_toolchain.cmake가 생성됩니다. Preset에서 이 경로를 지정합니다.
{
"name": "conan-release",
"inherits": "base",
"displayName": "Conan (Release)",
"toolchainFile": "${sourceDir}/build/conan_toolchain.cmake",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
}
빌드 순서:
# 1. Conan install (preset보다 먼저)
conan install . --output-folder=build --build=missing
# 2. CMake configure (preset 사용)
cmake --preset conan-release
# 3. Build
cmake --build build
주의: Conan의 --output-folder와 preset의 binaryDir가 일치해야 합니다. 위 예에서는 둘 다 build입니다.
vcpkg 바이너리 캐시 (2025년 이후 방식)
vcpkg는 포트를 소스에서 빌드하므로, 캐시가 없으면 CI 작업마다 Boost·OpenSSL 같은 의존성을 처음부터 컴파일합니다. 이걸 막는 것이 바이너리 캐시입니다. 한동안은 VCPKG_BINARY_SOURCES=clear;x-gha,readwrite 한 줄로 GitHub Actions 캐시를 쓰는 방법이 널리 퍼졌는데, GitHub가 캐시 서비스 API를 바꾸면서 이 방식이 동작하지 않게 되었고 vcpkg는 2025년에 x-gha 공급자를 제거했습니다. 인터넷 예제 대부분이 아직 이 설정을 쓰고 있어서, 복사해 넣으면 경고와 함께 캐시 없이 매번 전부 다시 빌드됩니다. 빌드 시간이 갑자기 수십 분 늘었다면 이것부터 확인하세요.
지금 쓸 수 있는 방법은 두 가지입니다.
방법 1: files 공급자 + actions/cache (설정이 가장 간단)
env:
VCPKG_BINARY_SOURCES: "clear;files,${{ github.workspace }}/.vcpkg-cache,readwrite"
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: .vcpkg-cache
key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json', 'vcpkg-configuration.json') }}
restore-keys: vcpkg-${{ runner.os }}-
- run: cmake --preset ci-release
- run: cmake --build --preset ci-release
restore-keys를 두면 vcpkg.json이 바뀌어도 이전 캐시를 받아 바뀐 포트만 다시 빌드합니다. 다만 GitHub Actions 캐시는 저장소당 용량 한도가 있어서, OS·트리플렛이 많은 매트릭스에서는 오래된 캐시가 금방 밀려납니다.
방법 2: GitHub Packages의 NuGet 피드 (vcpkg 팀 권장, 여러 저장소가 캐시 공유 가능)
env:
VCPKG_BINARY_SOURCES: "clear;nuget,https://nuget.pkg.github.com/<OWNER>/index.json,readwrite"
이 방식은 워크플로에서 GITHUB_TOKEN(packages: write 권한)으로 NuGet 소스를 등록하는 단계가 추가로 필요하고, Linux·macOS 러너에서는 mono가 있어야 합니다. 절차는 Microsoft Learn의 vcpkg “GitHub Packages로 바이너리 캐시” 문서를 따라 하면 됩니다.
VCPKG_BINARY_SOURCES는 CMake 캐시 변수가 아니라 환경 변수라는 점에 주의하세요. 프리셋에 넣을 때는 cacheVariables가 아니라 environment에 넣어야 vcpkg가 읽습니다.
{
"name": "ci-release",
"inherits": "vcpkg-default",
"environment": {
"VCPKG_BINARY_SOURCES": "clear;files,$env{GITHUB_WORKSPACE}/.vcpkg-cache,readwrite"
}
}
vcpkg + Conan 혼용 프로젝트 (팀 분리)
팀 내에서 vcpkg 사용자와 Conan 사용자가 공존할 때, 각자 자신의 preset만 사용하도록 분리합니다.
{
"version": 3,
"configurePresets": [
{
"name": "vcpkg-release",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/vcpkg-release",
"toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_CXX_STANDARD": "17"
}
},
{
"name": "conan-release",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/conan-release",
"toolchainFile": "${sourceDir}/build/conan_toolchain.cmake",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_CXX_STANDARD": "17"
}
}
]
}
preset 미발견, inherits 오류, toolchainFile 경로: 에러 해결
”Could not find a preset named “default""
원인: CMakePresets.json에 해당 이름의 preset이 없거나, CMakeUserPresets.json만 있고 CMakePresets.json이 없음.
해결법:
# preset 목록 확인
cmake --list-presets
CMakePresets.json이 프로젝트 루트에 있는지, configurePresets에 name: "default"가 있는지 확인하세요.
”Preset inherits from “base” which is not defined”
원인: inherits로 참조한 preset이 같은 파일 또는 include된 파일에 없음. 또는 CMakeUserPresets.json의 preset이 CMakePresets.json의 preset을 상속할 때, User 파일이 Presets를 include하지 않음.
해결법: CMakeUserPresets.json에서 상속하려면 include로 CMakePresets.json을 명시합니다 (버전 4+). 또는 User 파일이 있으면 Presets가 암시적으로 include되므로, Presets 파일에 base가 있는지 확인하세요.
”CMAKE_TOOLCHAIN_FILE” or “toolchainFile” 경로 오류
증상: Could not find toolchain file 또는 vcpkg.cmake not found.
원인: $env{VCPKG_ROOT}가 설정되지 않았거나, 경로가 잘못됩니다.
해결법:
# VCPKG_ROOT 확인
echo $VCPKG_ROOT
# 설정
export VCPKG_ROOT=/opt/vcpkg # 또는 Windows: set VCPKG_ROOT=C:\vcpkg
상대 경로 사용 시:
"toolchainFile": "${sourceDir}/cmake/toolchain.cmake"
“binaryDir”가 매번 덮어씌워짐
증상: debug와 release 빌드가 같은 build/를 쓰면서 설정이 꼬임.
원인: binaryDir를 ${sourceDir}/build로 고정해 둠.
해결법:
"binaryDir": "${sourceDir}/build/${presetName}"
각 preset마다 build/debug, build/release처럼 분리됩니다.
Windows에서 “Ninja Multi-Config” 관련 에러
증상: CMAKE_BUILD_TYPE=Release로 configure했는데 결과물이 Debug 폴더에 생기거나 최적화가 안 된 바이너리가 나옴. 또는 Generator Ninja does not support platform specification.
원인: Ninja Multi-Config는 CMAKE_BUILD_TYPE을 무시하고, --config를 주지 않으면 기본 설정(보통 Debug)으로 빌드합니다. 두 번째 오류는 Ninja 계열 생성기에 architecture.strategy: "set"을 준 경우입니다(“GCC / Clang / MSVC 멀티 컴파일러” 예제 참고).
해결법:
cmake --preset msvc
cmake --build build/msvc --config Release
또는 build preset에서 configuration 지정:
{
"name": "msvc-release",
"configurePreset": "msvc",
"configuration": "Release"
}
“condition”으로 숨긴 preset이 목록에 안 나옴
증상: cmake --list-presets에 Windows용 preset이 Linux에서 안 보임.
원인: condition이 false이면 해당 preset은 사용 불가.
해결법: 정상 동작입니다. 플랫폼별 preset은 해당 OS에서만 보이도록 설계된 것입니다.
cacheVariables가 적용 안 됨
증상: CMAKE_BUILD_TYPE=Release를 넣었는데 Debug로 빌드됩니다.
원인: 상속 순서. 자식 preset의 cacheVariables가 부모보다 우선합니다. 또는 Ninja Multi-Config에서는 CMAKE_BUILD_TYPE이 무시되고 --config가 우선합니다.
해결법: 단일 설정 생성기(Ninja, Makefile)에서는 CMAKE_BUILD_TYPE이 configure 시점에 적용됩니다. cmake --build 시 --config Release를 붙이거나, build preset에 configuration을 넣으세요.
Conan preset에서 “conan_toolchain.cmake not found”
원인: conan install을 하지 않았거나, --output-folder 경로가 preset의 binaryDir와 다름.
해결법:
# Conan install을 build 디렉터리에 출력
conan install . --output-folder=build --build=missing
# preset의 binaryDir가 build인지 확인
# toolchainFile: "${sourceDir}/build/conan_toolchain.cmake"
JSON 문법 오류
증상: Expecting ',' delimiter 또는 Unexpected token.
원인: JSON에서 마지막 요소 뒤에 쉼표를 넣으면 안 됨. "name": "x" 뒤에 ,만 있고 다음 필드가 없으면 오류.
해결법:
❌ 잘못된 예: "name": "default", 뒤에 쉼표만 있고 다음 필드 없음
{
"name": "default"
}
✅ 올바른 예: 마지막 필드 뒤에는 쉼표 없음
“version” 필드 누락 또는 잘못된 버전
증상: Unsupported version 또는 schema 검증 실패.
해결법: version은 정수입니다. 3 또는 4 이상 사용. cmakeMinimumRequired는 선택 사항이지만, 팀 협업 시 명시하면 좋습니다.
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 20,
"patch": 0
}
}
inherits 순환 참조
증상: Preset "A" inherits from "B" which inherits from "A" 또는 비슷한 순환 에러.
원인: A → B → A처럼 상속 체인이 순환함.
해결법: 상속 구조를 트리 형태로 유지. base → debug → debug-asan처럼 단방향으로만 상속.
condition에서 환경 변수 미설정
증상: $env{VCPKG_ROOT}를 condition에서 쓰는데, 변수가 없으면 preset이 비활성화되거나 에러.
원인: notEquals에서 $env{X}가 빈 문자열일 때 "rhs": ""와 비교하면, 변수 미설정 시 예상과 다르게 동작할 수 있음.
해결법: 환경 변수 의존 preset은 CMakeUserPresets.json에 두거나, CI에서 반드시 해당 변수를 export한 뒤 실행.
base preset 분리·User Presets·condition 활용 원칙
base preset 분리
공통 설정은 hidden base에 두며, 나머지는 inherits로 상속합니다. 중복을 줄이고 수정 지점을 한 곳으로 모읍니다.
{
"name": "base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_CXX_STANDARD": "17",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
}
binaryDir를 preset별로 분리
build/${presetName} 패턴을 사용하면 Debug/Release, 컴파일러별로 디렉터리가 분리되어 설정이 섞이지 않습니다.
환경 변수는 User Presets에
VCPKG_ROOT, CONAN_HOME 등 개발자마다 다른 경로는 CMakeUserPresets.json에 두고 Git에 넣지 않습니다.
// CMakeUserPresets.json
{
"version": 3,
"configurePresets": [
{
"name": "my-vcpkg",
"inherits": "vcpkg-default",
"toolchainFile": "/home/me/vcpkg/scripts/buildsystems/vcpkg.cmake"
}
]
}
cmakeMinimumRequired 명시
팀 전체가 동일한 CMake 버전 이상을 쓰도록 제한합니다.
"cmakeMinimumRequired": {
"major": 3,
"minor": 23,
"patch": 0
}
displayName, description 활용
IDE에서 preset 목록을 볼 때 사람이 읽기 쉬운 이름을 표시합니다.
{
"name": "vcpkg-release",
"displayName": "vcpkg Release",
"description": "vcpkg로 의존성 관리, Release 빌드"
}
condition으로 플랫폼 제한
Windows 전용 preset은 Windows에서만 보이게 합니다.
{
"name": "msvc",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
}
testPresets·buildPresets 쌍으로 정의
configure preset마다 대응하는 build·test preset을 두면 cmake --build --preset X, ctest --preset X로 일관되게 사용할 수 있습니다.
cacheVariables 타입 명시 (선택)
BOOL, PATH 등 타입을 명시하면 IDE에서 더 정확히 해석합니다: "BUILD_SHARED_LIBS": { "type": "BOOL", "value": "OFF" }.
GitHub Actions·workflowPresets·packagePresets
CI/CD 통합 (GitHub Actions)
name: Build
on: [push, pull_request]
jobs:
build-linux:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install CMake
uses: jwlawson/actions-setup-cmake@v2
with:
cmake-version: '3.28'
- name: Configure
run: cmake --preset ci-release
- name: Build
run: cmake --build --preset ci-release
CMakePresets.json에 ci-release preset 정의: "inherits": "base", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" }
CI 캐시로 빌드 시간 단축
빌드 디렉터리(build/) 전체를 actions/cache로 보존하는 예제가 많은데, 저는 권하지 않습니다. 캐시 키가 CMake 파일 해시라서 소스가 바뀌어도 같은 캐시를 복원하게 되고, 복원된 파일의 타임스탬프가 체크아웃한 소스보다 새것으로 보이면 증분 빌드가 바뀐 파일을 놓칠 수 있습니다. 실제로 “CI에서만 옛날 코드로 테스트가 돈다” 같은 이상한 현상을 겪고 나면 원인을 찾기도 어렵습니다. 컴파일 결과를 재사용하고 싶다면 입력 해시로 동작하는 ccache를 쓰는 편이 안전합니다.
- uses: hendrikmuhs/ccache-action@v1
with:
key: ${{ runner.os }}-${{ matrix.preset }}
- run: cmake --preset ci-release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- run: cmake --build --preset ci-release
의존성 빌드 시간은 앞에서 다룬 vcpkg 바이너리 캐시로, 우리 코드의 컴파일 시간은 ccache로 나눠서 줄이는 구성이 가장 예측 가능합니다.
멀티 플랫폼 CI (Linux, macOS, Windows)
strategy:
matrix:
include:
- { os: ubuntu-22.04, preset: linux-release }
- { os: macos-14, preset: macos-release }
- { os: windows-2022, preset: msvc-release }
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- run: cmake --preset ${{ matrix.preset }}
- run: cmake --build --preset ${{ matrix.preset }}
testPresets 활용
CMake 3.20+에서 ctest --preset default로 테스트 실행. 상세 예제는 앞의 testPresets 예제 참고.
workflowPresets (version 6, CMake 3.25+)
configure → build → test → package를 한 번에 실행합니다. 워크플로의 첫 단계는 반드시 configure여야 하고, 이후 단계의 프리셋은 모두 같은 configure 프리셋을 가리켜야 합니다.
{
"version": 6,
"workflowPresets": [
{
"name": "ci",
"steps": [
{ "type": "configure", "name": "ci-release" },
{ "type": "build", "name": "ci-release" },
{ "type": "test", "name": "ci-test" }
]
}
]
}
cmake --workflow --preset ci
CI 스크립트가 cmake --workflow --preset ci 한 줄로 줄어들어, 로컬에서 CI와 똑같은 순서를 재현하기 쉬워집니다.
include로 설정 분리
대형 프로젝트에서는 preset을 여러 파일로 나눌 수 있습니다 (버전 4+).
{
"version": 4,
"include": ["presets/linux.json", "presets/windows.json", "presets/ci.json"]
}
GitLab CI / Jenkins 연동
# .gitlab-ci.yml
build:
script:
- cmake --preset ci-release
- cmake --build --preset ci-release
- ctest --preset ci-test
// Jenkinsfile
sh 'cmake --preset ci-release && cmake --build --preset ci-release && ctest --preset ci-test'
packagePresets로 배포 패키지 생성 (version 6, CMake 3.25+)
{
"packagePresets": [
{
"name": "release-tgz",
"configurePreset": "release",
"generators": ["TGZ"]
}
]
}
cpack --preset release-tgz
도입 시 확인할 버전·CI·협업 요건
CMake Presets: CMake 3.19+ (configure), 3.20+ (build/test) · CMakePresets.json 루트 · CMakeUserPresets.json .gitignore · base (hidden) · binaryDir: ${sourceDir}/build/${presetName} · Debug/Release 분리 · vcpkg/Conan 시 toolchainFile · cmakeMinimumRequired · displayName, description
CI/CD: cmake --preset, cmake --build --preset · build/캐시 캐싱 · 플랫폼별 preset · ctest --preset
팀 협업: cmake --list-presets 안내 · README 문서화 · VCPKG_ROOT 등 환경 변수 문서화
기능별 요약
| 항목 | 설명 |
|---|---|
| CMakePresets.json | 프로젝트 공통, Git 커밋 |
| CMakeUserPresets.json | 개인 설정, .gitignore |
| inherits | base 상속으로 중복 제거 |
| binaryDir | ${sourceDir}/build/${presetName} |
| toolchainFile | vcpkg, Conan 연동 |
| condition | 플랫폼별 preset 제한 |
핵심: 팀 전체 동일 preset · base로 공통 설정 통합 · binaryDir preset별 분리 · vcpkg/Conan은 toolchainFile
자주 묻는 질문 (FAQ)
- CMake 3.19 미만? Configure 3.19+, Build/Test 3.20+ 필요.
- IDE에서 preset 안 보임? CLion 2022.2+, VS 2022 17.5+, VS Code CMake Tools. 루트에
CMakePresets.json·JSON 문법 확인. - vcpkg+Conan 동시 사용? 권장하지 않음. 하나 선택 후 preset 분리.
- CMakeUserPresets.json? Presets 복사 후 개인 경로 수정.
inherits로 상속. - preset 목록?
cmake --list-presets또는--all(hidden 포함).
참고 자료
- CMake Presets 공식 문서
- Visual Studio · CLion · VS Code CMake Tools CMake Presets로 팀 빌드 설정을 통일하고, vcpkg·Conan·CI/CD를 같은 프리셋 이름으로 연결할 수 있습니다.
관련 글
- CMake 입문: CMakeLists.txt 기초
- C++ 프로젝트용 GitHub Actions: 3개 OS 매트릭스, vcpkg 캐시
- CMake 3.28+ 프리셋과 모듈로 크로스 플랫폼 빌드 구성하기
- vcpkg 기초: Manifest 모드, Triplet, 버전 고정
- Conan 기초: conanfile, 프로필, CMake 연동
- CMake 에러 해결