vcpkg 기초: 설치, Manifest 모드, Triplet, 버전 고정, 오버레이 커스텀 포트
들어가며: “vcpkg가 뭔데 이렇게 복잡해요”
라이브러리 설치에서 막히는 순간
"find_package(fmt) failed — fmt를 못 찾아요"
"로컬에서는 되는데 CI에서만 빌드가 실패해요"
"팀원 A는 빌드되는데 B는 실패해요. vcpkg 버전이 다르대요"
"Boost 설치하는데 30분 넘게 걸려요"
"정적 링크로 배포해야 하는데 동적 링크만 되요"
"공식 레지스트리에 없는 라이브러리를 쓰고 싶어요"
"undefined reference 에러가 나는데 헤더는 잘 찾아요"
"vcpkg install 했는데 CMake가 여전히 못 찾아요"
이 글은 vcpkg 설치부터 Manifest 모드, Triplet, 버전 고정, 커스텀 포트까지를 예제와 함께 다룹니다. 실무에서 자주 부딪히는 에러와 CI 구성도 함께 정리합니다.
요구 환경: vcpkg 2024.01+, CMake 3.20+, C++17 이상
설치 지옥·CI 실패·팀원 간 차이: vcpkg가 필요한 상황
신규 프로젝트 — 라이브러리 설치 지옥
상황: C++ 프로젝트에 fmt, spdlog를 추가하려 함
문제: 수동으로 소스 다운로드·빌드·경로 설정 반복
결과: vcpkg Manifest 모드로 vcpkg.json에 의존성 선언 → 한 번에 해결
CI에서만 빌드 실패
상황: 로컬에서는 빌드 성공, GitHub Actions에서 find_package 실패
문제: 로컬에 Classic 모드로 설치한 패키지가 CI에 없음
결과: Manifest 모드 + vcpkg.json + CMAKE_TOOLCHAIN_FILE 지정
팀원마다 vcpkg 상태가 다름
상황: 팀원 A는 fmt 10.1, B는 10.2가 설치됨
문제: API 차이로 A는 되는데 B는 실패
결과: vcpkg.json의 builtin-baseline 고정 + 필요한 경우 overrides로 특정 버전 지정
정적 링크 배포 필요
상황: 단일 실행 파일로 배포해야 함 (DLL 없이)
문제: 기본 triplet은 동적 링크
결과: x64-windows-static triplet 사용
사내 라이브러리 통합
상황: 공식 레지스트리에 없는 사내 유틸리티 라이브러리
문제: vcpkg install로 설치 불가
결과: 오버레이 포트로 사내 포트 디렉터리 등록
시나리오별 기술 선택
| 시나리오 | vcpkg 기능 | 적용 방법 |
|---|---|---|
| 신규 프로젝트 | Manifest 모드 | vcpkg.json 생성 |
| CI 빌드 | Manifest + 툴체인 | CMAKE_TOOLCHAIN_FILE |
| 버전 통일 | builtin-baseline, overrides | vcpkg.json 커밋 |
| 정적 링크 | Triplet | VCPKG_TARGET_TRIPLET |
| 사내 라이브러리 | 오버레이 | VCPKG_OVERLAY_PORTS |
flowchart TD
subgraph 문제[실무 문제]
P1[라이브러리 설치 지옥] --> S1[Manifest 모드]
P2[CI 빌드 실패] --> S2[툴체인 + vcpkg.json]
P3[버전 불일치] --> S3[baseline + overrides]
P4[정적 링크] --> S4[Triplet]
P5[사내 라이브러리] --> S5[오버레이]
end
vcpkg 설치
설치 방법
vcpkg는 Git 저장소를 클론한 뒤 bootstrap 스크립트를 실행해 실행 파일을 만듭니다.
Linux/macOS:
# 1. vcpkg 클론 (프로젝트 밖 또는 프로젝트 내 서브모듈로)
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
# 2. bootstrap 실행
./bootstrap-vcpkg.sh
# 3. 설치 확인
./vcpkg version
Windows (PowerShell 또는 cmd):
# 1. vcpkg 클론
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
# 2. bootstrap 실행
.\bootstrap-vcpkg.bat
# 3. 설치 확인
.\vcpkg.exe version
설치 결과: vcpkg(또는 vcpkg.exe) 실행 파일이 생성됩니다. vcpkg 디렉터리에서 실행하거나, PATH에 추가해 전역에서 사용할 수 있습니다.
프로젝트에 vcpkg 서브모듈로 추가 (권장)
팀 전체가 동일한 vcpkg 버전을 쓰려면 프로젝트에 서브모듈로 추가합니다.
# 프로젝트 루트에서
git submodule add https://github.com/Microsoft/vcpkg.git vcpkg
# 서브모듈 초기화
git submodule update --init --recursive
# vcpkg bootstrap
cd vcpkg
./bootstrap-vcpkg.sh # Linux/macOS
# .\bootstrap-vcpkg.bat # Windows
cd ..
장점: 프로젝트와 vcpkg 버전이 함께 고정되어, “팀원 A는 되는데 B는 안 된다” 문제를 줄입니다.
사전 요구사항
| 항목 | 최소 버전 |
|---|---|
| Git | 2.x |
| CMake | 3.20+ |
| C++ 컴파일러 | MSVC 2019+, GCC 9+, Clang 10+ |
| Python | 3.8+ (일부 포트에서 필요) |
# CMake 버전 확인
cmake --version
# C++ 컴파일러 확인
g++ --version # Linux
clang++ --version # macOS
cl # Windows (Visual Studio Developer Command Prompt)
Manifest 모드로 의존성 선언하기
Classic 모드 vs Manifest 모드
| 구분 | Classic 모드 | Manifest 모드 |
|---|---|---|
| 설치 방식 | vcpkg install fmt 전역 설치 | vcpkg.json에 선언, CMake 설정 시 자동 설치 |
| 의존성 위치 | vcpkg 설치 디렉터리 | 프로젝트별 격리 |
| 재현성 | 낮음 (팀원마다 다름) | 높음 (vcpkg.json + builtin-baseline) |
| 권장 | 레거시 | 신규 프로젝트는 Manifest 모드 |
최소 Manifest 모드 예제
프로젝트 구조:
my-app/
├── CMakeLists.txt
├── vcpkg.json
├── vcpkg/ # git submodule (선택)
└── src/
└── main.cpp
vcpkg.json:
{
"name": "my-app",
"version": "1.0.0",
"description": "vcpkg Manifest 모드 예제",
"dependencies": [
"fmt",
"spdlog"
]
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.20)
project(my-app VERSION 1.0.0 LANGUAGES CXX)
# vcpkg 툴체인 지정 (필수!)
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake"
CACHE STRING "Vcpkg toolchain")
add_executable(my-app src/main.cpp)
find_package(fmt CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)
target_link_libraries(my-app PRIVATE
fmt::fmt
spdlog::spdlog
)
target_compile_features(my-app PRIVATE cxx_std_17)
src/main.cpp:
#include <spdlog/spdlog.h>
#include <fmt/core.h>
int main() {
spdlog::set_level(spdlog::level::debug);
spdlog::info("Hello, vcpkg! {}", fmt::format("vcpkg works!"));
return 0;
}
빌드 실행
# vcpkg가 서브모듈인 경우
cmake -B build -S . \
-DCMAKE_TOOLCHAIN_FILE="$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake"
cmake --build build
./build/my-app # 또는 build\my-app.exe (Windows)
vcpkg 패키지 다운로드 및 빌드 메커니즘:
cmake -DCMAKE_TOOLCHAIN_FILE=.../vcpkg.cmake 실행:
1. CMake가 toolchain 파일 로드:
vcpkg.cmake 내부:
- VCPKG_INSTALLED_DIR 설정
- vcpkg.json 파싱
- 의존성 목록 추출
2. vcpkg.json 파싱:
{
"dependencies": ["fmt", "spdlog"]
}
→ 의존성 그래프 생성:
app
├─ fmt
└─ spdlog
3. 포트 메타데이터 조회:
각 패키지에 대해:
a. 레지스트리에서 포트 검색:
vcpkg/ports/fmt/vcpkg.json
vcpkg/ports/spdlog/vcpkg.json
b. 버전 정보 읽기:
vcpkg/versions/f-/fmt.json
→ "version": "10.1.1"
→ "git-tree": "abc123def456..."
c. 포트 파일 다운로드:
git clone (sparse checkout)
OR git tree object 읽기
4. 전이적 의존성 해석:
spdlog/vcpkg.json:
{
"dependencies": ["fmt"]
}
최종 그래프:
app
├─ fmt (직접)
└─ spdlog
└─ fmt (전이) → 중복 제거
5. Package ID (해시) 계산:
Package ID = hash(
패키지명,
버전,
Triplet,
포트 파일 내용,
의존성 해시
)
예: fmt/10.1.1/x64-linux
→ package_id: 1a2b3c4d5e6f...
6. 캐시 확인:
~/.cache/vcpkg/archives/ (Linux/Mac)
%LOCALAPPDATA%\vcpkg\archives\ (Windows)
if (캐시에 package_id 존재):
→ 압축 해제만 (빠름!)
else:
→ 소스 다운로드 + 빌드
7. 소스 다운로드:
portfile.cmake 읽기:
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO fmtlib/fmt
REF 10.1.1
SHA512 abc123...
)
동작:
a. GitHub에서 tarball 다운로드:
https://github.com/fmtlib/fmt/archive/10.1.1.tar.gz
b. SHA512 검증:
downloaded_sha512 == expected_sha512?
→ 불일치 시 에러
c. 압축 해제:
downloads/fmt-10.1.1/
→ buildtrees/fmt/src/10.1.1-xxx/
8. 빌드 실행:
portfile.cmake:
vcpkg_cmake_configure(
SOURCE_PATH ${SOURCE_PATH}
OPTIONS
-DFMT_DOC=OFF
-DFMT_TEST=OFF
)
vcpkg_cmake_build()
vcpkg_cmake_install()
내부 동작:
a. CMake 설정:
cd buildtrees/fmt/x64-linux-rel
cmake ../../src/ \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=packages/fmt_xxx
b. 빌드:
cmake --build . --target install
c. 설치:
packages/fmt_xxx/
├── include/
│ └── fmt/
├── lib/
│ └── libfmt.a
└── share/
└── fmt/
└── fmtConfig.cmake
9. 패키지 압축 및 캐시:
packages/fmt_xxx/
→ 압축: archives/1a2b3c4d5e6f.zip
→ 캐시 저장 (다음에 재사용)
10. 설치 디렉터리로 복사:
packages/fmt_xxx/
→ installed/x64-linux/
├── include/fmt/
├── lib/libfmt.a
└── share/fmt/fmtConfig.cmake
11. find_package 실행:
find_package(fmt CONFIG REQUIRED)
CMake 검색 경로:
- installed/x64-linux/share/fmt/
→ fmtConfig.cmake 발견!
fmtConfig.cmake 로드:
- fmt::fmt 타겟 생성
- 헤더 경로: installed/x64-linux/include
- 라이브러리: installed/x64-linux/lib/libfmt.a
12. target_link_libraries:
target_link_libraries(my-app PRIVATE fmt::fmt)
→ CMake가 fmt::fmt의 속성 사용:
- INTERFACE_INCLUDE_DIRECTORIES
- INTERFACE_LINK_LIBRARIES
→ my-app에 자동 전파
빌드 디렉터리 구조:
vcpkg_root/
├── downloads/ # 소스 tarball
│ └── fmt-10.1.1.tar.gz
├── buildtrees/ # 빌드 중간 산물
│ └── fmt/
│ └── src/
│ └── x64-linux-rel/
├── packages/ # 빌드 완료 (설치 전)
│ └── fmt_xxx/
├── installed/ # 최종 설치 경로
│ └── x64-linux/
│ ├── include/
│ ├── lib/
│ └── share/
└── .cache/ (archives/) # 바이너리 캐시
Triplet별 빌드:
x64-windows (동적):
→ installed/x64-windows/bin/fmt.dll
→ installed/x64-windows/lib/fmt.lib
x64-windows-static:
→ installed/x64-windows-static/lib/fmt.lib (정적)
병렬 빌드:
fmt와 nlohmann_json은 독립적:
→ 동시 빌드 가능!
spdlog는 fmt 의존:
→ fmt 완료 후 spdlog 빌드
vcpkg는 자동으로 의존성 순서 관리
동작 흐름:
- CMake가
CMAKE_TOOLCHAIN_FILE을 로드 - vcpkg 툴체인이 vcpkg.json을 읽음
- fmt, spdlog가 없으면 자동 설치
- find_package가 vcpkg 설치 경로에서 패키지 검색
- 빌드 완료
Manifest 모드 — 의존성 객체 형태
버전 제약, 플랫폼별 의존성을 지정할 때는 객체 형태입니다.
{
"name": "my-app",
"version": "1.0.0",
"dependencies": [
"fmt",
{
"name": "spdlog",
"version>=": "1.11.0"
},
{
"name": "openssl",
"platform": "windows"
}
]
}
| 필드 | 설명 |
|---|---|
name | 패키지 이름 |
version>= | 최소 버전 (이 버전 이상). 상한 제약 문법은 없음 |
platform | 플랫폼 조건 (windows, !windows, linux, osx) |
features | 포트의 선택 기능 (예: Boost의 일부 모듈, curl의 SSL 백엔드) |
features는 빌드 시간과 바이너리 크기에 직접 영향을 줍니다. 예를 들어 "boost"를 통째로 적으면 수십 개 라이브러리를 모두 빌드하므로, 실제로 쓰는 것만 boost-filesystem처럼 개별 포트로 적거나 기능을 지정하는 편이 CI 시간을 크게 줄입니다. 반대로 어떤 포트는 기본 기능(default features)에 필요한 것이 빠져 있어 링크 단계에서 심볼이 없다는 에러가 나기도 하는데, 이때는 vcpkg search <포트>나 포트의 vcpkg.json에서 기능 목록을 확인해 명시해 줍니다.
{
"dependencies": [
{ "name": "curl", "features": ["ssl"] },
{ "name": "fmt", "default-features": false }
]
}
툴체인 파일을 -DCMAKE_TOOLCHAIN_FILE=... 대신 CMakeLists.txt 안에서 지정하려면 반드시 project() 호출보다 먼저 set(CMAKE_TOOLCHAIN_FILE ... CACHE STRING "")를 두어야 합니다. project()에서 컴파일러와 툴체인이 결정되기 때문에, 그 뒤에 설정하면 조용히 무시되고 find_package가 vcpkg 패키지를 찾지 못합니다. 한 번 구성된 빌드 디렉터리는 캐시에 옛 값이 남아 있으므로, 설정을 바꾼 뒤에는 빌드 디렉터리를 지우고 다시 구성하는 것이 안전합니다. 팀에서는 이 설정을 CMakePresets.json에 넣어 두는 방식(아래 운영 패턴의 CMakePresets.json)이 가장 실수가 적습니다.
Triplet과 정적·동적 링크
Triplet이란?
Triplet은 arch-vendor-os 형식으로, 대상 플랫폼·아키텍처·빌드 타입을 지정합니다.
| Triplet | 설명 |
|---|---|
x64-windows | Windows 64비트, 동적 링크 |
x64-windows-static | Windows 64비트, 정적 링크 |
x64-linux | Linux 64비트 |
x64-osx | macOS Intel |
arm64-osx | macOS Apple Silicon (M1/M2) |
Triplet 지정 방법
방법 1: CMake 옵션
cmake -B build -S . \
-DCMAKE_TOOLCHAIN_FILE="$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake" \
-DVCPKG_TARGET_TRIPLET=x64-windows-static
방법 2: 환경 변수
export VCPKG_DEFAULT_TRIPLET=x64-windows-static
cmake -B build -S .
방법 3: CMakeLists.txt에서 project() 전에 지정
set(VCPKG_TARGET_TRIPLET "x64-windows-static" CACHE STRING "")
project(my-app LANGUAGES CXX)
vcpkg.json이나 vcpkg-configuration.json에는 triplet을 지정하는 필드가 없습니다. triplet은 CMake 캐시 변수(VCPKG_TARGET_TRIPLET), 환경 변수, 또는 아래 운영 패턴에서 다루는 CMakePresets.json으로 넘기는 것이 정석입니다.
정적 링크 vs 동적 링크
| 용도 | Triplet |
|---|---|
| 개발 (빌드 속도 우선) | x64-windows, x64-linux |
| 배포 (단일 실행 파일) | x64-windows-static, x64-linux-static |
Triplet 확인
# 현재 기본 triplet
vcpkg version
# 설치된 패키지 확인 (triplet별)
vcpkg list
builtin-baseline과 overrides로 버전 관리
builtin-baseline
builtin-baseline은 vcpkg 포트 저장소의 특정 커밋 해시입니다. 이 해시에 따라 사용 가능한 패키지 버전이 결정됩니다.
{
"name": "my-app",
"version": "1.0.0",
"dependencies": ["fmt", "spdlog"],
"builtin-baseline": "a1b2c3d4e5f6789012345678901234567890abc"
}
baseline 해시 가져오기:
cd vcpkg
git pull
git rev-parse HEAD
# 출력: a1b2c3d4e5f6789012345678901234567890abc
권장: 프로덕션에서는 baseline을 고정하며, 별도 브랜치에서 업데이트 테스트 후 반영합니다.
lock 파일이 없는 이유 — baseline이 곧 잠금
npm의 package-lock.json이나 Cargo의 Cargo.lock에 익숙하면 vcpkg에서도 lock 파일을 찾게 되지만, vcpkg에는 lock 파일이 없습니다. 대신 버전 결정이 결정적(deterministic)으로 설계되어 있습니다. builtin-baseline이 가리키는 커밋의 versions/baseline.json이 각 포트의 기준 버전을 정하고, 같은 vcpkg.json + 같은 baseline이면 누가 언제 빌드하든 같은 버전이 선택됩니다. 즉 baseline이 들어 있는 vcpkg.json을 커밋하는 것 자체가 버전을 잠그는 행위입니다.
처음 vcpkg를 도입했을 때 저도 “lock 파일은 어디 생기지?”라며 빌드 디렉터리를 뒤진 적이 있습니다. 이 구조를 이해하고 나서는 baseline 커밋 해시를 코드 리뷰 대상으로 다루게 됐습니다. baseline을 바꾸는 PR 하나가 곧 “명시하지 않은 모든 의존성의 업그레이드”이기 때문입니다.
버전 제약 문법 — version>=와 overrides
vcpkg의 제약은 최소 버전(version>=)만 있습니다. version< 같은 상한 문법은 존재하지 않으며, 넣으면 매니페스트 검증 단계에서 에러가 납니다. 해석 규칙은 “baseline 버전과 모든 version>= 제약 중 가장 높은 최소값을 고른다”입니다. 새 버전이 나와도 baseline을 올리지 않는 한 자동으로 따라 올라가지 않으므로, 상한이 필요한 상황 자체가 드뭅니다.
{
"name": "my-app",
"version": "1.0.0",
"dependencies": [
{ "name": "spdlog", "version>=": "1.11.0" },
"fmt"
],
"builtin-baseline": "a1b2c3d4e5f6789012345678901234567890abc",
"overrides": [
{ "name": "fmt", "version": "10.1.1" }
]
}
version>=: 이 버전 이상을 요구합니다. baseline 버전이 더 높으면 baseline 버전이 선택됩니다.overrides: 제약 계산을 무시하고 정확히 이 버전을 쓰게 합니다. 전이 의존성까지 포함해 특정 버전을 고정하거나, 새 버전의 회귀 때문에 낮은 버전으로 묶어 둘 때 씁니다. 필요하면"port-version"으로 포트 리비전까지 지정합니다.version>=나overrides를 쓰려면builtin-baseline(또는vcpkg-configuration.json의 default-registry baseline)이 있어야 합니다. baseline 없이 쓰면 버전 제약에 baseline이 필요하다는 에러가 납니다.
커스텀 포트(오버레이)
오버레이란?
오버레이는 vcpkg 공식 레지스트리보다 우선 적용되는 포트 디렉터리입니다. 사내 라이브러리, 수정된 포트, 아직 upstream되지 않은 패키지를 사용할 때 씁니다.
오버레이 디렉터리 구조
my-ports/
├── internal-lib/
│ ├── vcpkg.json
│ └── portfile.cmake
└── patched-spdlog/
├── vcpkg.json
├── portfile.cmake
└── patches/
└── fix-logging.patch
최소 오버레이 포트 예제 (헤더-온리)
my-ports/header-only-lib/vcpkg.json:
{
"name": "header-only-lib",
"version": "1.0.0",
"description": "헤더 전용 유틸리티 라이브러리",
"license": "MIT",
"dependencies": []
}
my-ports/header-only-lib/portfile.cmake:
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO example/header-only-lib
REF "v1.0.0"
SHA512 0
HEAD_REF main
)
# 헤더만 복사 (빌드 없음)
file(INSTALL "${SOURCE_PATH}/include/"
DESTINATION "${CURRENT_PACKAGES_DIR}/include"
FILES_MATCHING PATTERN "*.hpp"
)
file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage"
DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE")
주의: SHA512 0으로 두고 설치 시도 → 에러 메시지에 실제 해시가 출력됨 → 복사해 넣기.
오버레이 사용
cmake -B build -S . \
-DCMAKE_TOOLCHAIN_FILE="$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake" \
-DVCPKG_OVERLAY_PORTS="$(pwd)/my-ports"
vcpkg.json에서 오버레이 포트를 의존성으로 추가:
{
"dependencies": [
"fmt",
"header-only-lib"
]
}
vcpkg-configuration.json으로 오버레이 등록
프로젝트 루트에 vcpkg-configuration.json을 두면 CMake 옵션 없이 오버레이가 적용됩니다.
{
"default-registry": {
"kind": "git",
"repository": "https://github.com/microsoft/vcpkg",
"baseline": "a1b2c3d4e5f6..."
},
"overlay-ports": ["./my-ports"]
}
Manifest·정적 링크·오버레이 예제 프로젝트
기본 Manifest 모드
vcpkg-basics-demo/
├── CMakeLists.txt
├── vcpkg.json
├── vcpkg/ # git submodule
└── src/
└── main.cpp
vcpkg.json:
{
"name": "vcpkg-basics-demo",
"version": "1.0.0",
"description": "vcpkg 기초 예제",
"dependencies": [
"fmt",
"spdlog",
"nlohmann-json"
],
"builtin-baseline": "a1b2c3d4e5f6789012345678901234567890abc"
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.20)
# 툴체인은 반드시 project()보다 먼저 지정
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake"
CACHE STRING "Vcpkg toolchain")
project(vcpkg-basics-demo VERSION 1.0.0 LANGUAGES CXX)
add_executable(demo src/main.cpp)
find_package(fmt CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)
target_link_libraries(demo PRIVATE
fmt::fmt
spdlog::spdlog
nlohmann_json::nlohmann_json
)
target_compile_features(demo PRIVATE cxx_std_17)
src/main.cpp:
#include <spdlog/spdlog.h>
#include <fmt/core.h>
#include <nlohmann/json.hpp>
int main() {
spdlog::info("vcpkg 기초 예제");
nlohmann::json j = {{"name", "vcpkg"}, {"version", "1.0"}};
spdlog::info("JSON: {}", j.dump());
return 0;
}
Triplet + 정적 링크
cmake -B build -S . \
-DCMAKE_TOOLCHAIN_FILE="$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake" \
-DVCPKG_TARGET_TRIPLET=x64-windows-static \
-DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
오버레이 + Manifest
my-project/
├── CMakeLists.txt
├── vcpkg.json
├── vcpkg-configuration.json
├── vcpkg/
├── my-ports/
│ └── internal-lib/
│ ├── vcpkg.json
│ └── portfile.cmake
└── src/
└── main.cpp
vcpkg-configuration.json:
{
"default-registry": {
"kind": "git",
"repository": "https://github.com/microsoft/vcpkg",
"baseline": "a1b2c3d4e5f6..."
},
"overlay-ports": ["./my-ports"]
}
패키지 설정 파일 없음, baseline 누락, 빌드 실패: 에러 해결
“Could not find a package configuration file provided by ‘fmt’”
CMake Error: Could not find a package configuration file provided by "fmt"
원인: CMAKE_TOOLCHAIN_FILE을 지정하지 않았거나, vcpkg가 패키지를 아직 빌드하지 않음.
해결법:
# 1. 툴체인 파일 반드시 지정
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE="$(pwd)/vcpkg/scripts/buildsystems/vcpkg.cmake"
# 2. build 폴더 삭제 후 재시도 (캐시된 잘못된 설정 제거)
rm -rf build
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=...
“Port xxx is not in the baseline”
Error: Could not find a version that satisfies the requirement ...
원인: builtin-baseline이 오래되었거나, 해당 패키지가 baseline에 없음.
해결법:
cd vcpkg
git pull
git rev-parse HEAD # 이 해시를 vcpkg.json의 builtin-baseline에 넣기
{
"builtin-baseline": "최신_커밋_해시"
}
“Building package xxx failed”
Building package spdlog:x64-linux failed
원인: 패키지 빌드 중 컴파일 에러, 의존성 누락, 네트워크 오류 등. 해결법:
# 상세 로그로 원인 파악
export VCPKG_VERBOSE=1
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=...
# 특정 패키지만 수동 빌드
./vcpkg install spdlog --debug
“A suitable version of cmake was not found”
Error: vcpkg was unable to find the version of cmake in your PATH
원인: PATH에 CMake가 없거나 버전이 낮음 (3.20+ 권장). 해결법:
which cmake
cmake --version
# 3.20 이상이어야 함. PATH에 추가하거나 최신 CMake 설치
“undefined reference” / 링크 에러
undefined reference to `spdlog::...'
원인: target_link_libraries에 누락, 또는 정적/동적 링크 혼용.
해결법:
# find_package 후 반드시 target_link_libraries에 추가
find_package(spdlog CONFIG REQUIRED)
target_link_libraries(my-app PRIVATE spdlog::spdlog)
“C++ 표준 불일치”
error: #error "spdlog requires C++17 or later"
원인: 프로젝트가 C++14로 빌드되는데 의존성이 C++17 요구. 해결법:
target_compile_features(my-app PRIVATE cxx_std_17)
# 또는
set(CMAKE_CXX_STANDARD 17)
오버레이 포트를 찾을 수 없음
Error: Could not find port internal-lib
원인: VCPKG_OVERLAY_PORTS 경로가 잘못되었거나, 포트 디렉터리 구조가 맞지 않음.
해결법:
# 경로 확인 (절대 경로 권장)
ls -la my-ports/internal-lib/vcpkg.json
cmake -B build -S . \
-DVCPKG_OVERLAY_PORTS="$(pwd)/my-ports"
요청한 버전이 버전 데이터베이스에 없음
증상: version>=나 overrides에 적은 버전을 찾을 수 없다는 에러로 설치가 멈춥니다.
원인: 적은 버전이 현재 vcpkg 클론의 versions/ 데이터베이스에 없습니다. 오타이거나, vcpkg 서브모듈이 오래되어 그 버전의 포트가 아직 등록되지 않은 경우입니다.
해결법:
# 해당 포트에 등록된 버전 목록 확인
cat vcpkg/versions/f-/fmt.json | head -40
# vcpkg 서브모듈을 갱신한 뒤 baseline도 함께 올리기
cd vcpkg && git pull && cd ..
./vcpkg/vcpkg x-update-baseline
CI에서만 “Could NOT find” 발생
원인: 로컬에 Classic 모드로 설치한 패키지에 의존. CI에는 해당 패키지가 없음.
해결법: Manifest 모드로 전환. vcpkg.json에 의존성 선언하고 CMAKE_TOOLCHAIN_FILE 지정.
”vcpkg integrate install” 했는데 안 됨
원인: integrate install은 Classic 모드용. Manifest 모드는 CMAKE_TOOLCHAIN_FILE을 명시적으로 지정해야 함.
해결법: Manifest 모드에서는 -DCMAKE_TOOLCHAIN_FILE=...를 반드시 CMake 설정 시 전달합니다.
Manifest·baseline·서브모듈 운용 원칙
Manifest 모드 사용
vcpkg.json을 프로젝트 루트에 두고 Git에 커밋- Classic 모드는 레거시. 신규 프로젝트는 Manifest 모드
builtin-baseline 고정
- 재현 가능한 빌드를 위해 특정 커밋 해시 사용
- 업데이트 시 별도 브랜치에서 테스트 후 main 반영
예외적 고정은 overrides로
- baseline으로 전체 버전을 맞추고, 특정 포트만 다른 버전이 필요하면
overrides에 명시 - overrides를 넣은 이유를 커밋 메시지에 남겨 두어 나중에 제거 시점을 판단
vcpkg 서브모듈
git submodule add https://github.com/Microsoft/vcpkg.git vcpkg
- vcpkg 버전을 프로젝트와 함께 고정
- CI에서
submodules: recursive로 체크아웃
find_package CONFIG 모드
find_package(fmt CONFIG REQUIRED)
target_link_libraries(my-app PRIVATE fmt::fmt)
- vcpkg 패키지는 대부분 Config 모드.
CONFIG명시 권장
의존성 최소화
- 꼭 필요한 패키지만 추가
- 헤더 전용 라이브러리(nlohmann-json 등)는 빌드 없이 사용 가능
CI 캐시 활용
buildtrees,packages,downloads캐시hashFiles('vcpkg.json', 'vcpkg-configuration.json')를 key에 포함
CMake Presets
CMakePresets.json으로 팀 전체 설정 통일cmake --preset vcpkg-default로 간편 설정
멀티 플랫폼 CI·캐시·사내 오버레이 공유
멀티 플랫폼 CI 매트릭스
# .github/workflows/build.yml
strategy:
matrix:
include:
- os: ubuntu-latest
triplet: x64-linux
- os: windows-latest
triplet: x64-windows-static
- os: macos-latest
triplet: arm64-osx
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Configure
run: |
cmake -B build -S . \
-DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake \
-DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }}
CI vcpkg 캐시
- name: Cache vcpkg
uses: actions/cache@v4
with:
path: |
${{ github.workspace }}/vcpkg/buildtrees
${{ github.workspace }}/vcpkg/packages
${{ github.workspace }}/vcpkg/downloads
key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json', 'vcpkg-configuration.json') }}
restore-keys: vcpkg-${{ runner.os }}-
CMakePresets.json
{
"version": 3,
"configurePresets": [
{
"name": "vcpkg-default",
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake"
}
},
{
"name": "vcpkg-static",
"inherits": "vcpkg-default",
"cacheVariables": {
"VCPKG_TARGET_TRIPLET": "x64-windows-static"
}
}
]
}
의존성 업데이트 전략
1. 개발: 주기적으로 baseline 업데이트, 로컬 테스트
2. 스테이징: 업데이트 후 CI 전체 통과 확인
3. 프로덕션: builtin-baseline이 고정된 vcpkg.json 커밋으로 버전 고정
사내 오버레이 공유
회사 레포지토리: company/vcpkg-ports
각 프로젝트에서:
-DVCPKG_OVERLAY_PORTS="$(pwd)/../vcpkg-ports"
또는 서브모듈로 vcpkg-ports 포함
vcpkg 도입 점검 항목
vcpkg 기초 설정
- vcpkg 설치 (bootstrap 완료)
- vcpkg 서브모듈 추가 (권장)
- CMAKE_TOOLCHAIN_FILE 지정
- Manifest 모드 + vcpkg.json
버전 관리
- builtin-baseline 고정
- 필요한 경우만 버전 제약 (version>=, overrides) 사용
Triplet
- 배포용 정적 링크 시 x64-windows-static 등 사용
- 팀 전체 triplet 통일
오버레이 (사내 라이브러리)
- my-ports/ 디렉터리 구조
- VCPKG_OVERLAY_PORTS 또는 vcpkg-configuration.json
CI/CD
- submodules: recursive
- vcpkg 캐시 (buildtrees, packages, downloads)
- hashFiles(‘vcpkg.json’, ‘vcpkg-configuration.json’) key
자주 묻는 질문 (FAQ)
Q. vcpkg Classic 모드와 Manifest 모드 차이는?
A. Classic 모드는 vcpkg install로 전역 설치. Manifest 모드는 프로젝트 vcpkg.json에 의존성을 선언하고 CMake 설정 시 자동 설치. 재현 가능한 빌드를 위해 Manifest 모드를 권장합니다.
Q. CMAKE_TOOLCHAIN_FILE을 매번 지정해야 하나요?
A. CMakePresets.json에 cacheVariables로 넣어 두면 cmake --preset vcpkg-default로 한 번에 설정됩니다. IDE(CLion, VS)에서도 preset을 선택하면 됩니다.
Q. vcpkg에는 lock 파일이 없나요?
A. 없습니다. vcpkg.json의 builtin-baseline이 모든 포트의 기준 버전을 결정하고, 같은 매니페스트와 baseline이면 항상 같은 버전이 선택되므로 vcpkg.json을 커밋하는 것이 곧 버전 고정입니다. 특정 포트만 다른 버전이 필요하면 overrides를 씁니다.
Q. 사내 라이브러리를 vcpkg로 쓰려면?
A. 오버레이 포트를 만듭니다. my-ports/내라이브러리/에 vcpkg.json과 portfile.cmake를 두며, -DVCPKG_OVERLAY_PORTS로 경로를 지정합니다. 자세한 내용은 vcpkg 패키지 만들기를 참고하세요.
Q. CI 빌드가 너무 느려요.
A. actions/cache로 vcpkg의 buildtrees, packages, downloads를 캐시하세요. hashFiles('vcpkg.json', 'vcpkg-configuration.json')를 key에 넣으면 의존성 변경 시에만 캐시가 갱신됩니다.
Q. builtin-baseline을 언제 업데이트하나요?
A. 보안 패치·버그 수정이 필요할 때. 별도 브랜치에서 업데이트 후 전체 테스트를 돌리고, 통과하면 main에 반영합니다. vcpkg Manifest 모드 + vcpkg.json + CMAKE_TOOLCHAIN_FILE 지정만으로 대부분의 C++ 의존성 관리 문제를 해결할 수 있습니다. 다음 글: [C++ #53-3] vcpkg 고급 활용 | Manifest·Triplet·오버레이·바이너리 캐시 이전 글: Visual Studio로 C++ 프로파일링·메모리 진단하기
같이 보면 좋은 글
- C++ vcpkg 고급 활용 | Manifest·Triplet·오버레이·바이너리 캐시 가이드
- vcpkg 포트 직접 만들기
- Conan 기초
- CMake 입문 | 수십 개 파일 컴파일할 때 필요한 빌드 자동화 (CMakeLists.txt 기초)
- C++ 패키지 관리 실무: vcpkg와 Conan으로 외부 라이브러리 의존성 지옥 탈출 [#40-1]