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/ConantoolchainFile로 자동 지정
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추가된 주요 기능
23.20build·test 프리셋
33.21condition, toolchainFile, installDir, generator 생략 가능
43.23include로 다른 프리셋 파일 합치기
63.25workflowPresets, 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설명용도
equalslhs == rhsOS, 아키텍처 비교
notEqualslhs != rhs환경 변수 미설정 시 제외
inListstring이 list에 포함여러 OS 지원 (Linux, Darwin)
notInListstring이 list에 미포함특정 OS 제외
matchesstring이 regex와 매칭버전·경로 패턴
notMatchesstring이 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
inheritsbase 상속으로 중복 제거
binaryDir${sourceDir}/build/${presetName}
toolchainFilevcpkg, 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 포함).

참고 자료


관련 글