이번 글에서는 모던 CMake를 활용하여 재사용 가능한 라이브러리를 패키지화하고, 다른 프로젝트에서 손쉽게 사용할 수 있도록 설정하는 방법을 알아보겠습니다. CMake의 패키지 관리 기능을 통해 라이브러리를 배포하고, find_package()를 이용하여 의존성을 간편하게 관리할 수 있습니다. 이 과정을 통해 프로젝트의 확장성과 유지보수성을 크게 향상시킬 수 있습니다.
CMake 패키지 구성의 필요성
재사용 가능한 라이브러리를 패키지화하면 다음과 같은 이점을 얻을 수 있습니다:
- 의존성 관리의 단순화: find_package()를 통해 필요한 라이브러리를 손쉽게 찾고 링크할 수 있습니다.
- 버전 관리: 특정 버전의 라이브러리를 요구하고, 호환성을 보장할 수 있습니다.
- 배포의 용이성: 라이브러리를 다른 개발자나 프로젝트와 쉽게 공유할 수 있습니다.
- 모듈화: 프로젝트를 여러 개의 독립적인 컴포넌트로 분리하여 관리할 수 있습니다.
패키지 구성의 기본 흐름
- 라이브러리 정의: CMake로 라이브러리를 정의하고, 필요한 설정을 추가합니다.
- 설치 규칙 설정: install() 명령어를 사용하여 라이브러리와 헤더 파일을 설치할 위치를 지정합니다.
- 패키지 설정 파일 생성: export() 명령어를 통해 패키지 설정 파일을 생성합니다.
- 패키지 찾기 설정: 다른 프로젝트에서 find_package()를 사용하여 패키지를 찾고 링크할 수 있도록 설정합니다.
예제 프로젝트 구성
디렉토리 구조
my_library/
├── CMakeLists.txt
├── include/
│ └── mylib.h
├── src/
│ └── mylib.cpp
└── tests/
└── test.cpp
최상위 CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(MyLibrary VERSION 1.0.0 LANGUAGES CXX)
# C++ 표준 설정
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 서브디렉토리 추가
add_subdirectory(src)
add_subdirectory(tests)
# 패키지 설치 설정
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/MyLibraryConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/MyLibraryConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/MyLibraryConfig.cmake"
INSTALL_DESTINATION lib/cmake/MyLibrary
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/MyLibraryConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/MyLibraryConfigVersion.cmake"
DESTINATION lib/cmake/MyLibrary
)
src/CMakeLists.txt
add_library(MyLib src/mylib.cpp)
# 공개 헤더 파일 경로 설정
target_include_directories(MyLib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
$<INSTALL_INTERFACE:include>
)
# 라이브러리 설치 규칙 설정
install(TARGETS MyLib EXPORT MyLibraryTargets
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
PUBLIC_HEADER DESTINATION include
)
# 헤더 파일 설치 규칙 설정
install(DIRECTORY ../include/ DESTINATION include)
# Export 설정
install(EXPORT MyLibraryTargets
FILE MyLibraryTargets.cmake
NAMESPACE MyLibrary::
DESTINATION lib/cmake/MyLibrary
)
cmake/MyLibraryConfig.cmake.in
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/MyLibraryTargets.cmake")
tests/CMakeLists.txt
enable_testing()
find_package(MyLibrary CONFIG REQUIRED)
add_executable(MyTests test.cpp)
target_link_libraries(MyTests PRIVATE MyLibrary::MyLib)
add_test(NAME MyTest COMMAND MyTests)
src/mylib.cpp 예제
#include "mylib.h"
int add(int a, int b) {
return a + b;
}
include/mylib.h 예제
#pragma once
int add(int a, int b);
tests/test.cpp 예제
#include <gtest/gtest.h>
#include "mylib.h"
TEST(MyLibTest, AddFunction) {
EXPECT_EQ(add(1, 2), 3);
EXPECT_EQ(add(-1, -1), -2);
}
패키지 설치 및 사용
라이브러리 빌드 및 설치
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=/usr/local ..
cmake --build .
cmake --install .
- CMAKE_INSTALL_PREFIX: 라이브러리가 설치될 경로를 지정합니다. 기본값은 /usr/local입니다.
다른 프로젝트에서 패키지 사용
디렉토리 구조
external_project/
├── CMakeLists.txt
└── src/
└── main.cpp
external_project/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(ExternalProject LANGUAGES CXX)
find_package(MyLibrary CONFIG REQUIRED)
add_executable(ExternalApp src/main.cpp)
target_link_libraries(ExternalApp PRIVATE MyLibrary::MyLib)
external_project/src/main.cpp 예제
#include <iostream>
#include "mylib.h"
int main() {
std::cout << "1 + 2 = " << add(1, 2) << std::endl;
return 0;
}
빌드 및 실행
mkdir build
cd build
cmake -DCMAKE_PREFIX_PATH=/usr/local ..
cmake --build .
./ExternalApp
- CMAKE_PREFIX_PATH: find_package()가 패키지 설정 파일을 찾을 경로를 지정합니다. 라이브러리가 설치된 경로를 포함해야 합니다.
패키지 버전 관리
패키지 버전 관리는 프로젝트 간 호환성을 유지하는 데 중요합니다. CMake는 패키지 버전을 명시하고, 요구되는 최소 버전을 설정할 수 있습니다.
find_package()에서 버전 요구
find_package(MyLibrary 1.0.0 CONFIG REQUIRED)
- 1.0.0: 최소 요구 버전을 지정합니다.
- REQUIRED: 패키지를 찾지 못하면 빌드를 중단합니다.
패키지 설정 파일에서 버전 정보 사용
MyLibraryConfig.cmake.in 파일에 다음 내용을 추가하여 버전 정보를 포함시킬 수 있습니다.
include(CMakeFindDependencyMacro)
find_dependency(Boost REQUIRED COMPONENTS system)
# 패키지 타겟 설정
add_library(MyLibrary::MyLib ALIAS MyLib)
패키지 재배포
패키지를 재배포하려면, 설정한 설치 규칙에 따라 패키지를 생성하고 배포할 수 있습니다. CPack을 사용하여 다양한 형식의 패키지를 생성할 수 있습니다.
CPack 설정 추가
최상위 CMakeLists.txt에 다음을 추가합니다.
include(CPack)
set(CPACK_PACKAGE_NAME "MyLibrary")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
set(CPACK_PACKAGE_CONTACT "support@mycompany.com")
set(CPACK_GENERATOR "TGZ;DEB;RPM;ZIP")
include(CPack)
패키지 생성
cmake --build . --target package
- CPACK_GENERATOR에 지정된 형식으로 패키지가 생성됩니다.
베스트 프랙티스
- 타겟 기반 설정 사용: target_include_directories(), target_compile_definitions(), target_link_libraries() 등 타겟 기반 설정을 사용하여 설정을 명확하게 관리합니다.
- 인터페이스 라이브러리 활용: 공통 설정이나 기능을 인터페이스 라이브러리로 분리하여 재사용성을 높입니다.
- 버전 고정: 패키지의 버전을 명시하고, 의존성의 최소 요구 버전을 설정하여 호환성을 유지합니다.
- 명확한 설치 규칙: 헤더 파일과 라이브러리 파일의 설치 경로를 명확하게 지정하여 패키지를 일관되게 관리합니다.
- 패키지 설정 파일의 일관성 유지: EXPORT와 find_package()가 올바르게 동작하도록 설정 파일을 일관되게 유지합니다.
참고 자료
- CMake 공식 문서: Installing and Exporting
CMake Install - CMake 공식 문서: Packages
CMake Packaging - CMakePackageConfigHelpers 모듈
CMakePackageConfigHelpers - CMake Find Package Tutorial
Find Package Tutorial - CMake CPack Documentation
CPack Documentation - Modern CMake Best Practices
Modern CMake Best Practices - CMake Modules and Export Targets
CMake Modules - CMake Tutorial: Writing a Config File
Writing a Config File
이상으로, 모던 CMake를 활용하여 재사용 가능한 라이브러리를 패키지화하고 다른 프로젝트에서 쉽게 사용할 수 있도록 설정하는 방법을 살펴보았습니다. 이러한 패키지 관리 전략을 통해 프로젝트의 확장성과 유지보수성을 크게 향상시킬 수 있습니다.
반응형
'개발 이야기 > CMake' 카테고리의 다른 글
[모던 CMake] OpenCL 프로젝트 구성과 빌드 (0) | 2024.12.13 |
---|---|
[모던 CMake] Vulkan 프로젝트 구성과 빌드 (0) | 2024.12.13 |
[모던 CMake] Qt 프로젝트 구성과 빌드 (1) | 2024.12.11 |
[모던 CMake] CUDA 프로젝트 구성과 빌드 (0) | 2024.12.10 |
[모던 CMake] Python 바인딩 프로젝트 구성과 설정 (0) | 2024.12.09 |