모던 CMake와 타겟 기반 빌드 시스템

CMake의 초기 버전은 소스 파일과 바이너리를 나열하고 컴파일러 옵션을 지정하는 방식으로 작동 하였다. 그러나 "모던 CMake"는 타겟(target) 중심의 빌드 시스템을 도입 하였다. 타겟은 실행 파일이나 라이브러리와 같은 최종 산출물을 의미한다. 타겟 중심 접근 방식에서는 컴파일러 옵션, 포함 디렉토리, 링크 라이브러리 등이 타겟에 연관되어 정의된다. 이는 재사용성과 유지보수성을 크게 향상시킨다.

타겟 기반 CMake에서는 add_executable 또는 add_library 명령어로 타겟을 정의한 후, target_include_directories, target_link_libraries, target_compile_definitions 등의 명령어로 타겟에 속성을 추가한다. 이는 각 타겟의 의존성을 명확하게 관리할 수 있게 한다.

인터페이스 라이브러리와 인터페이스 타겟

인터페이스 라이브러리는 소스 파일 없이 컴파일러 옵션, 포함 경로, 정의 등을 전파하는 데 사용된다. add_library 명령어를 이용하여 INTERFACE 키워드와 함께 정의된다. 인터페이스 라이브러리는 주로 특정 옵션이나 경로를 여러 타겟에 걸쳐 쉽게 공유하기 위해 사용된다.

예를 들어, 특정 타겟에만 적용되어야 하는 컴파일러 플래그가 있을 때, 이 플래그를 인터페이스 라이브러리로 정의하고, 해당 타겟에 연결하여 간편하게 적용할 수 있다. 이는 코드 중복을 줄이고 유지보수를 쉽게 한다.

임포트된 타겟과 외부 라이브러리

임포트된 타겟은 외부 라이브러리를 CMake 프로젝트 내에서 마치 로컬 타겟처럼 사용할 수 있게 한다. IMPORTED 키워드를 사용하여 타겟을 정의한 후, IMPORTED_LOCATION, IMPORTED_INCLUDE_DIRECTORIES, IMPORTED_LINK_LIBRARIES 등의 속성을 설정한다. 이렇게 하면, 외부 라이브러리의 경로나 설정이 변경되더라도 CMake 설정 파일에서 한 번에 관리할 수 있다.

이 방법은 특히 시스템 라이브러리나 서드 파티 라이브러리를 사용할 때 유용하다. 외부 라이브러리의 인터페이스를 임포트된 타겟으로 정의하면, CMake의 종속성 추적 메커니즘과 완벽하게 통합된다.

CMake를 이용한 컴파일러 및 링커 설정

CMake는 다양한 컴파일러와 플랫폼을 지원한다. 특정 컴파일러 또는 플랫폼에 맞게 프로젝트를 설정하려면, CMAKE_C_COMPILER, CMAKE_CXX_COMPILER 등의 변수로 컴파일러를 지정할 수 있다. 또한, target_compile_options 명령어로 타겟별로 컴파일러 옵션을 설정할 수 있다.

링커 설정의 경우, target_link_options 명령어를 사용하여 타겟별로 링커 옵션을 추가할 수 있다. 이러한 방법으로 다양한 환경에서 일관된 빌드를 보장할 수 있다.

빌드 타입 및 빌드 설정

CMake는 기본적으로 Debug, Release, RelWithDebInfo, MinSizeRel의 네 가지 빌드 타입을 지원한다. CMAKE_BUILD_TYPE 변수를 사용하여 빌드 타입을 설정할 수 있다. 각 빌드 타입에 대한 컴파일러 및 링커 옵션은 CMAKE_C_FLAGS_<BUILD_TYPE>, CMAKE_CXX_FLAGS_<BUILD_TYPE> 등의 변수로 조정할 수 있다.

여기서 <BUILD_TYPE>Debug, Release 등으로 대체된다. 이 방법으로 각 빌드 타입에 맞는 최적화 수준, 디버깅 정보 포함 여부 등을 세밀하게 제어할 수 있다.

커스텀 명령어와 커스텀 타겟

CMake는 add_custom_commandadd_custom_target 명령어를 통해 커스텀 빌드 명령어와 타겟을 정의할 수 있다. 커스텀 명령어는 파일 생성, 스크립트 실행, 외부 도구 호출 등의 작업을 자동화하는 데 사용된다. 커스텀 타겟은 특정 작업을 묶어 관리하는 데 유용하다.

예를 들어, 특정 파일을 컴파일하기 전에 특정 스크립트를 실행해야 하는 경우, 이를 커스텀 명령어로 정의할 수 있다. 이러한 방식으로 복잡한 빌드 워크플로우를 효율적으로 관리할 수 있다.

테스트 및 CTest와의 통합

CMake는 CTest라는 테스트 드라이버와 통합되어 있다. enable_testing 명령어로 프로젝트 내 테스트를 활성화하고, add_test 명령어로 개별 테스트를 정의할 수 있다. CTest는 다양한 플랫폼에서 일관된 테스트 환경을 제공하며, 빌드와 테스트를 자동화하는 데 강력한 도구이다.

테스트는 프로젝트의 품질을 보장하는 데 중요한 역할을 한다. CTest는 병렬 테스트, 타임아웃 설정, 결과 수집 등의 기능을 제공하여 고급 테스트 시나리오를 지원한다.

패키지 구성 및 설치

CMake를 사용하여 패키지 구성을 쉽게 할 수 있다. install 명령어를 사용하여 타겟, 파일, 디렉토리 등을 설치할 수 있다. 또한, export 명령어를 사용하여 패키지 구성 파일을 생성할 수 있다.

이를 통해, 다른 프로젝트에서 CMake 패키지를 간편하게 사용할 수 있게 된다. 특히, 타겟 기반 접근 방식을 사용하면 설치 및 패키징 작업이 훨씬 간소화된다.

FetchContent 및 ExternalProject

CMake는 외부 프로젝트를 관리하기 위한 도구로 FetchContentExternalProject를 제공한다. FetchContent는 간단한 외부 프로젝트를 다운로드하고 포함하는 데 사용된다. ExternalProject는 보다 복잡한 외부 프로젝트를 관리하고, 빌드, 설치 등의 작업을 수행하는 데 적합한다.

FetchContent는 간단한 경우에 사용되며, 종속성 관리가 비교적 쉽다. 반면, ExternalProject는 보다 복잡한 의존성을 처리할 수 있지만, 설정이 더 복잡한다.

모듈화 및 프로젝트 구성 파일

CMake는 프로젝트를 모듈화하여 관리하기 위한 다양한 기능을 제공한다. include 명령어를 사용하여 외부 CMake 스크립트를 포함할 수 있으며, find_package 명령어를 통해 외부 패키지를 검색하고 사용할 수 있다.

모듈화는 프로젝트를 확장하고 유지보수하는 데 중요한 역할을 한다. 프로젝트 구성 파일은 다른 프로젝트와의 연동을 간소화하고, 재사용 가능한 CMake 코드를 작성할 수 있도록 도와준다.

런타임 종속성 및 RPATH 설정

CMake는 런타임 종속성을 관리하기 위한 다양한 옵션을 제공한다. 특히, RPATH 설정을 통해 실행 파일이나 라이브러리가 런타임에 참조할 라이브러리 경로를 지정할 수 있다. CMAKE_INSTALL_RPATH, CMAKE_BUILD_WITH_INSTALL_RPATH 등의 변수를 사용하여 RPATH를 설정할 수 있다.

RPATH 설정은 특히 복잡한 종속성을 가진 프로젝트에서 중요하다. 잘못된 RPATH 설정은 런타임 오류를 유발할 수 있으므로, 주의 깊게 설정해야 한다.


관련 자료: - Kitware CMake Documentation, https://cmake.org/documentation/ - Modern CMake Guidelines by Henry Schreiner, https://cliutils.gitlab.io/modern-cmake/