CMake
1. 개요
1. 개요
CMake는 C와 C++ 프로젝트를 위한 대표적인 크로스 플랫폼 빌드 자동화 도구이다. 소프트웨어의 빌드, 테스트, 패키징 과정을 관리하기 위해 설계되었다. 킷웨어가 개발하여 2000년에 최초로 등장했으며, 이후 다양한 운영 체제와 컴파일러를 지원하는 표준적인 빌드 시스템 생성 도구로 자리 잡았다.
CMake의 핵심 역할은 플랫폼에 독립적인 설정 파일(CMakeLists.txt)을 읽고, 해당 플랫폼에 맞는 네이티브 빌드 시스템 파일을 생성하는 것이다. 이를 통해 개발자는 윈도우의 비주얼 스튜디오 솔루션, 리눅스나 macOS의 Makefile, 그리고 이클립스나 Xcode 프로젝트 파일 등을 동일한 소스 코드 베이스에서 생성할 수 있다. 이는 다중 구성 빌드, 예를 들어 디버그와 릴리스 버전을 손쉽게 관리할 수 있게 한다.
주요 용도는 복잡한 C/C++ 프로젝트의 빌드 프로세스를 단순화하고 표준화하는 것이다. CMake는 프로젝트 내부의 소스 코드와 헤더 파일 간의 의존성을 자동으로 분석하며, 타사 라이브러리를 검색하고 통합하는 강력한 기능을 제공한다. 이로 인해 대규모 오픈 소스 프로젝트부터 상업적 응용 소프트웨어 개발에 이르기까지 널리 채택되고 있다.
CMake는 현대적인 소프트웨어 개발 워크플로우, 특히 CI/CD 파이프라인과 깊이 연관되어 있다. 빌드 스크립트의 일관성을 보장함으로써 다양한 환경에서의 반복적이고 신뢰할 수 있는 빌드를 가능하게 하며, 이는 지속적 통합과 지속적 배포의 핵심 요소가 된다.
2. 기본 개념
2. 기본 개념
2.1. CMakeLists.txt 파일
2.1. CMakeLists.txt 파일
CMake 프로젝트의 구성과 빌드 과정은 CMakeLists.txt라는 이름의 텍스트 파일에 의해 정의된다. 이 파일은 프로젝트의 루트 디렉토리와 각 하위 디렉토리에 위치할 수 있으며, 빌드 시스템이 생성될 때 최상위 파일부터 순차적으로 처리된다. 파일 내에는 프로젝트의 메타데이터를 선언하고, 소스 코드 파일을 지정하며, 실행 파일이나 라이브러리 같은 빌드 타겟을 생성하는 명령어들이 기록된다.
CMakeLists.txt 파일의 핵심 구성 요소는 CMake 명령어이다. 가장 기본적인 명령어로는 project()가 있으며, 이를 통해 프로젝트의 이름과 사용하는 프로그래밍 언어를 정의한다. add_executable() 명령어는 소스 파일 목록을 바탕으로 실행 가능한 프로그램을 생성하는 타겟을 정의하고, add_library() 명령어는 정적 라이브러리나 공유 라이브러리를 빌드하도록 지시한다. 또한 target_link_libraries() 명령어를 사용하면 생성된 타겟이 다른 라이브러리에 의존성을 가지도록 연결할 수 있다.
이 파일의 강력한 기능 중 하나는 조건부 처리와 반복 작업을 지원한다는 점이다. if(), foreach() 같은 제어 흐름 명령어를 활용하면 운영체제, 컴파일러, 또는 사용자 정의 변수에 따라 다른 빌드 옵션을 적용할 수 있다. 이를 통해 단일 설정 파일로 리눅스, 윈도우, macOS 등 다양한 플랫폼에 맞는 Makefile이나 IDE 프로젝트 파일을 생성하는 크로스 플랫폼 빌드가 가능해진다.
CMakeLists.txt 파일은 계층적으로 구성될 수 있다. 프로젝트 루트의 메인 파일에서 add_subdirectory() 명령어를 사용하면 하위 디렉토리로 이동하여 해당 경로의 CMakeLists.txt 파일을 처리한다. 이 방식은 대규모 프로젝트에서 모듈별로 빌드 로직을 분리하고 관리하는 데 유용하며, 소프트웨어 아키텍처를 반영한 깔끔한 디렉토리 구조를 유지하는 데 도움을 준다.
2.2. 변수와 캐시
2.2. 변수와 캐시
CMake에서 변수는 값을 저장하고 참조하기 위한 기본 메커니즘이다. 변수는 set() 명령어를 사용해 정의되며, ${변수명} 구문으로 값을 참조한다. 이러한 변수는 일반적으로 스크립트 내에서 임시적인 값을 저장하거나, 흐름 제어를 위한 조건으로 사용된다. 변수의 유효 범위는 해당 변수가 정의된 디렉토리와 그 하위 디렉토리로 제한되는 것이 기본 규칙이다.
보다 영구적이고 전역적인 설정을 관리하기 위해 CMake는 캐시 변수 시스템을 제공한다. 캐시 변수는 set() 명령어에 CACHE 옵션을 추가하여 정의하며, CMakeCache.txt 파일에 저장되어 빌드 시스템 생성 사이에 그 값이 유지된다. 이는 컴파일러 경로, 라이브러리 검색 경로, 사용자가 정의한 옵션(예: BUILD_SHARED_LIBS)과 같은 시스템 전반의 설정을 저장하는 데 필수적이다.
캐시 변수는 주로 find_package()나 find_library() 같은 명령어가 검색한 결과를 저장하거나, option() 명령어를 통해 사용자에게 빌드 옵션을 제공할 때 사용된다. 캐시 변수의 값은 초기 실행 시 CMake GUI나 터미널의 -D 옵션을 통해 덮어쓰기 할 수 있어, 사용자 맞춤형 빌드 구성을 가능하게 한다. 변수와 캐시의 명확한 구분과 적절한 사용은 효율적이고 유지보수 가능한 CMake 스크립트 작성의 핵심이다.
2.3. 명령어와 함수
2.3. 명령어와 함수
CMake는 빌드 과정을 제어하기 위해 다양한 내장 명령어와 함수를 제공한다. 이 명령어들은 CMakeLists.txt 파일에 작성되며, 빌드 시스템이 생성하는 Makefile이나 IDE 프로젝트 파일의 내용을 정의하는 역할을 한다. 가장 기본적인 명령어로는 project()를 통해 프로젝트 이름과 사용 언어를 선언하고, cmake_minimum_required()로 필요한 CMake의 최소 버전을 명시한다.
주요 명령어는 크게 타겟 정의, 파일 관리, 흐름 제어, 속성 설정 등으로 구분된다. add_executable()과 add_library()는 각각 실행 파일과 정적 라이브러리 또는 동적 라이브러리 타겟을 생성하는 핵심 명령어이다. target_include_directories()와 target_link_libraries()는 특정 타겟에 헤더 파일 검색 경로와 연결할 라이브러리를 지정할 때 사용된다. find_package()는 시스템에 설치된 타사 라이브러리를 검색하고 그 정보를 변수에 담는 데 쓰인다.
함수와 매크로를 사용하면 반복되는 명령어 조합을 재사용할 수 있어 스크립트를 모듈화하고 관리하기 쉽게 만든다. function()과 endfunction()으로 사용자 정의 함수를 만들 수 있으며, macro()와 endmacro()로 매크로를 정의할 수 있다. 함수는 새로운 변수 스코프를 생성하는 반면, 매크로는 호출한 곳의 스코프에 그대로 코드를 치환한다는 차이점이 있다. 또한 include() 명령어를 사용하면 다른 CMake 모듈 파일의 내용을 현재 스크립트에 삽입할 수 있다.
조건문과 반복문은 빌드 로직에 유연성을 더한다. if(), elseif(), else(), endif() 구문을 통해 변수 값이나 조건식을 평가할 수 있다. foreach()와 endforeach(), while()과 endwhile() 루프를 이용하면 리스트나 조건에 따른 반복 작업을 수행할 수 있다. 이러한 명령어와 함수들의 조합을 통해 개발자는 단순한 라이브러리 빌드부터 복잡한 크로스 플랫폼 애플리케이션의 빌드 과정까지 효율적으로 기술할 수 있다.
2.4. 타겟(Target)
2.4. 타겟(Target)
CMake에서 타겟은 빌드 시스템의 핵심 구성 요소이다. 타겟은 빌드 과정에서 생성되는 최종 산출물이나 중간 작업 단위를 의미하며, 실행 파일, 정적 라이브러리, 동적 라이브러리 등이 이에 해당한다. 개발자는 add_executable() 또는 add_library() 같은 명령어를 사용하여 이러한 타겟을 정의한다. 각 타겟은 소스 파일 목록, 컴파일 옵션, 링크 라이브러리, 포함 디렉토리 등과 같은 속성들을 가지며, 이는 빌드 시스템이 해당 타겟을 올바르게 구성하고 생성하는 데 필요한 정보를 제공한다.
타겟 간의 종속성은 빌드 순서와 링크 과정을 관리하는 데 중요하다. 예를 들어, 하나의 실행 파일 타겟이 특정 라이브러리 타겟에 의존한다면, target_link_libraries() 명령어를 사용하여 이 관계를 명시적으로 선언한다. 이를 통해 CMake는 라이브러리를 먼저 빌드하고 실행 파일을 링크하는 올바른 순서를 보장한다. 또한, 인터페이스 라이브러리와 같은 특수한 타겟 유형은 소스 파일 없이 컴파일 옵션이나 포함 디렉토리 같은 속성만을 정의하여 다른 타겟에 전파하는 용도로 사용된다.
타겟 지향 접근 방식은 빌드 명세를 모듈화하고 재사용성을 높인다. 각 타겟의 속성은 자체적으로 관리되며, target_include_directories()나 target_compile_options() 같은 명령어를 통해 공개적 또는 비공개적으로 설정할 수 있다. 이는 대규모 프로젝트에서 디렉토리 구조를 체계적으로 관리하고, 외부 패키지나 서드파티 라이브러리를 통합하는 데 유용하다. 결과적으로, 타겟 개념은 CMake가 복잡한 크로스 플랫폼 빌드 환경을 효율적으로 생성할 수 있는 기반을 마련한다.
3. 작성 방법
3. 작성 방법
3.1. 프로젝트 정의
3.1. 프로젝트 정의
CMake를 사용한 프로젝트의 시작은 CMakeLists.txt 파일 내에서 project() 명령어를 통해 프로젝트를 정의하는 것이다. 이 명령어는 빌드 시스템에 프로젝트의 기본 메타데이터를 설정하며, 프로젝트의 이름, 버전, 사용 프로그래밍 언어 등을 지정한다. 예를 들어, project(MyApp VERSION 1.0 LANGUAGES C CXX)와 같이 작성하면 "MyApp"이라는 이름의 프로젝트가 정의되고, C와 C++ 언어를 사용한다고 선언된다. 이 정의는 이후 변수 PROJECT_NAME과 PROJECT_VERSION에 값을 제공하는 기초가 된다.
프로젝트 정의는 최소한의 필수 구성 요소로, CMakeLists.txt 파일의 최상단에 위치하는 것이 일반적이다. project() 명령어는 CMake에게 작업 디렉토리의 최상위 레벨을 알리는 역할도 하며, 이를 통해 CMake는 프로젝트 관련 여러 기본 변수와 설정을 초기화한다. 특히, 프로젝트에 사용할 언어를 명시적으로 지정하면 해당 언어의 컴파일러를 검색하고 필요한 표준을 설정하는 과정이 활성화된다.
프로젝트 정의 시 버전 정보를 포함하면, 이후 CPack을 이용한 소프트웨어 패키징이나 CTest를 통한 테스트 보고 시 일관된 정보를 활용할 수 있다. 또한, 단일 CMakeLists.txt 파일 안에 여러 개의 project() 명령어를 사용하여 복잡한 서브프로젝트 구조를 관리할 수도 있지만, 대규모 프로젝트에서는 주로 add_subdirectory() 명령어를 통해 독립적인 하위 디렉토리별로 프로젝트를 구성하는 방식을 선호한다.
3.2. 실행 파일 및 라이브러리 추가
3.2. 실행 파일 및 라이브러리 추가
CMake에서 실행 파일과 라이브러리를 추가하는 작업은 add_executable()과 add_library() 명령어를 통해 이루어진다. 이 명령어들은 CMakeLists.txt 파일 내에서 프로젝트의 핵심 빌드 타겟을 정의한다. add_executable() 명령어는 최종적으로 생성될 실행 가능한 프로그램의 이름과 이를 구성하는 소스 코드 파일들을 지정한다. 예를 들어, add_executable(MyApp main.cpp helper.cpp)와 같이 작성하면 'MyApp'이라는 실행 파일이 main.cpp와 helper.cpp 파일을 컴파일하여 만들어지게 된다.
정적 라이브러리나 공유 라이브러리를 생성하기 위해서는 add_library() 명령어를 사용한다. 이 명령어는 라이브러리의 이름, 타입, 그리고 소스 파일 목록을 인자로 받는다. 라이브러리 타입은 STATIC, SHARED, MODULE 중 하나로 명시할 수 있으며, 생략 시 CMake가 현재 플랫폼의 기본 설정이나 전역 변수에 따라 타입을 결정한다. 예를 들어, add_library(MyLib STATIC lib.cpp)는 'MyLib'이라는 정적 라이브러리 파일을 생성한다.
생성된 실행 파일과 라이브러리는 서로 의존성을 가질 수 있다. 한 타겟이 다른 타겟의 코드를 사용해야 할 경우, target_link_libraries() 명령어를 사용하여 링크 관계를 설정한다. 이 명령어는 실행 파일이 필요한 라이브러리를 링크하거나, 한 라이브러리가 다른 라이브러리에 의존하는 경우에 사용된다. 예를 들어, target_link_libraries(MyApp PRIVATE MyLib)는 MyApp 실행 파일을 빌드할 때 MyLib 라이브러리를 링크하도록 지시한다. 여기서 PRIVATE 키워드는 의존성의 전파 범위를 제어하는 CMake의 현대적 타겟 사용 패러다임의 일부이다.
이러한 기본 명령어들을 조합하면 소규모 프로젝트부터 대규모 모듈식 소프트웨어에 이르기까지 다양한 구조의 빌드 시스템을 구성할 수 있다. 라이브러리를 먼저 정의하고, 이를 링크하는 실행 파일을 추가하는 방식은 코드의 재사용성을 높이고 빌드 프로세스를 체계적으로 관리하는 데 기여한다.
3.3. 디렉토리 구조 관리
3.3. 디렉토리 구조 관리
CMake 프로젝트의 디렉토리 구조 관리는 add_subdirectory() 명령어를 핵심으로 한다. 이 명령어는 현재 CMakeLists.txt 파일이 위치한 디렉토리에서 지정한 하위 디렉토리로 이동하여 해당 경로의 CMakeLists.txt 파일을 실행한다. 이를 통해 프로젝트를 논리적인 모듈이나 컴포넌트 단위로 분리하여 관리할 수 있으며, 각 하위 디렉토리는 자신만의 라이브러리나 실행 파일 타겟(Target)을 정의할 수 있다. 상위 디렉토리의 CMakeLists.txt는 프로젝트의 전반적인 설정과 하위 모듈 간의 의존성을 조정하는 역할을 한다.
효율적인 구조 관리를 위해 CMAKE_SOURCE_DIR과 CMAKE_BINARY_DIR 같은 CMake 내장 변수를 활용한다. CMAKE_SOURCE_DIR은 최상위 소스 디렉토리의 절대 경로를, CMAKE_BINARY_DIR은 빌드가 수행될 최상위 빌드 디렉토리의 경로를 제공한다. 소스 트리와 빌드 트리를 분리하는 아웃오브소스 빌드를 구성할 때 이 변수들을 사용하여 상대 경로를 명확하게 지정할 수 있다. 또한 include_directories()나 target_include_directories() 명령어를 사용해 헤더 파일이 위치한 디렉토리를 포함 경로에 추가함으로써, 복잡한 디렉토리 구조 속에서도 소스 파일이 필요한 헤더를 정확히 찾을 수 있도록 보장한다.
대규모 프로젝트에서는 라이브러리나 실행 파일의 소스 파일을 특정 디렉토리에서 자동으로 수집하는 패턴이 자주 사용된다. file(GLOB ...) 명령어를 이용해 *.cpp 같은 패턴으로 소스 파일 목록을 생성할 수 있으나, CMake 공식 문서는 빌드 시스템이 새로 추가된 소스 파일을 자동으로 인식하지 못할 수 있어 명시적인 파일 목록 작성을 권장한다. 최종적으로 잘 구성된 디렉토리 구조는 프로젝트의 확장성을 높이고, 모듈 간 의존성을 명확히 하며, 크로스 플랫폼 빌드를 보다 용이하게 만든다.
3.4. 외부 종속성 처리
3.4. 외부 종속성 처리
CMake 프로젝트에서 외부 라이브러리나 도구에 대한 종속성을 처리하는 방법은 다양하다. 가장 기본적인 접근법은 find_package 명령어를 사용하는 것이다. 이 명령어는 시스템에 설치된 패키지를 검색하여 해당 패키지의 헤더 파일 경로, 라이브러리 파일, 컴파일 정의 등의 정보를 변수로 제공한다. 예를 들어, OpenGL이나 Boost와 같은 널리 사용되는 라이브러리를 찾을 때 자주 활용된다. CMake는 자체적으로 많은 표준 패키지에 대한 검색 모듈을 포함하고 있어, 복잡한 설정 없이도 간편하게 통합할 수 있다.
시스템 패키지 관리자로 설치되지 않은 라이브러리를 사용해야 할 경우, CMake는 외부 프로젝트를 직접 다운로드하고 빌드하는 기능을 제공한다. FetchContent 모듈이나 ExternalProject_Add 명령어를 사용하면 Git 저장소나 아카이브 파일에서 소스 코드를 가져와 빌드 과정 중에 구성할 수 있다. 이 방식은 프로젝트의 이식성을 높이고, 특정 버전의 종속성을 보장하는 데 유용하다. 특히 CI/CD 파이프라인에서 재현 가능한 빌드를 구성할 때 중요한 역할을 한다.
보다 현대적인 접근법으로는 CMake의 패키지 관리 기능을 활용하는 것이다. find_package의 CONFIG 모드는 라이브러리가 CMake 패키지 설정 파일을 제공할 때 이를 사용하여 종속성을 선언적으로 처리할 수 있게 한다. 또한, vcpkg나 Conan 같은 외부 C++ 패키지 매니저와의 통합도 점차 보편화되고 있다. 이러한 도구들은 자체적인 종속성 해결 메커니즘을 가지고 있으며, CMake는 이들과 연동하여 검색된 패키지 정보를 사용할 수 있다. 이를 통해 복잡한 다중 라이브러리 종속성 그래프를 효율적으로 관리할 수 있다.
4. 빌드 시스템 생성
4. 빌드 시스템 생성
4.1. Makefile 생성
4.1. Makefile 생성
CMake의 핵심 기능은 다양한 플랫폼과 환경에서 사용할 수 있는 실제 빌드 시스템 파일을 생성하는 것이다. 이 과정에서 가장 기본적이고 널리 사용되는 출력 형식이 바로 Makefile이다. 사용자는 CMakeLists.txt 파일에 빌드 명세를 작성한 후, cmake 명령어를 실행하여 해당 소스 코드 디렉토리에 맞는 Makefile을 생성한다. 이렇게 생성된 Makefile은 이후 표준 make 명령어를 통해 프로젝트의 컴파일과 링크를 수행하는 데 사용된다.
Makefile 생성 과정은 일반적으로 별도의 빌드 디렉토리(Out-of-source build)에서 이루어진다. 사용자는 cmake <소스 경로> 명령을 실행하면, CMake는 해당 플랫폼의 컴파일러 (예: GCC, Clang)와 링커를 탐색하고, 시스템에 설치된 라이브러리를 찾아 최적의 빌드 설정을 구성한다. 이 모든 정보가 Makefile로 변환되어, 사용자는 단순히 make 명령만으로 프로젝트를 빌드할 수 있게 된다.
CMake는 단일 Makefile이 아닌, 복잡한 프로젝트 구조에 맞는 일련의 Makefile들을 생성한다. 이는 상위 디렉토리의 주 Makefile이 하위 디렉토리의 Makefile을 재귀적으로 호출하는 방식으로 동작하며, 라이브러리와 실행 파일 간의 종속성을 정확히 관리한다. 또한 다중 구성 빌드를 지원하여, 하나의 소스 트리에서 디버그 빌드와 릴리스 빌드 등 서로 다른 최적화 설정을 가진 Makefile을 동시에 생성하고 관리할 수 있다.
이러한 접근 방식은 개발자로 하여금 플랫폼별 Makefile의 복잡한 문법을 직접 작성할 필요 없이, 추상화된 CMake 스크립트에만 집중할 수 있게 한다. 결과적으로, 리눅스, macOS 등 유닉스 계열 시스템에서 널리 쓰이는 GNU Make 도구와의 호환성을 유지하면서도, 빌드 설정의 일관성과 이식성을 크게 향상시킨다.
4.2. IDE 프로젝트 파일 생성
4.2. IDE 프로젝트 파일 생성
CMake의 주요 강점 중 하나는 단일 CMakeLists.txt 파일로부터 다양한 통합 개발 환경의 네이티브 프로젝트 파일을 생성할 수 있다는 점이다. 이 기능은 개발자가 선호하는 IDE에서 편리하게 프로젝트를 열고 작업할 수 있게 하면서도, 빌드 설정의 일관성을 유지하도록 돕는다. CMake는 -G 옵션을 통해 원하는 제너레이터를 지정하여 해당 IDE에 맞는 프로젝트 파일을 출력한다.
주요 IDE에 대한 프로젝트 파일 생성 예시는 다음과 같다. 이는 플랫폼과 설치된 도구에 따라 사용 가능한 제너레이터가 달라진다.
IDE / 빌드 시스템 | CMake 제너레이터 이름 | 생성되는 프로젝트 파일 형식 |
|---|---|---|
|
| |
|
| |
|
| |
|
| |
|
|
이 과정은 일반적으로 빌드 디렉토리를 생성한 후, 해당 디렉토리에서 CMake를 실행하여 이루어진다. 예를 들어, Visual Studio 2022용 64비트 프로젝트를 생성하려면 명령줄에서 cmake -G "Visual Studio 17 2022" -A x64 ..와 같은 명령을 사용한다. 생성된 프로젝트 파일은 소스 코드와 분리된 빌드 디렉토리에 위치하므로, 소스 트리를 깨끗하게 유지할 수 있다. 이를 통해 개발자는 익숙한 IDE 인터페이스에서 코드 탐색, 디버깅, 빌드 구성을 관리할 수 있으며, 동시에 크로스 플랫폼 호환성을 확보할 수 있다.
4.3. 크로스 플랫폼 빌드
4.3. 크로스 플랫폼 빌드
CMake의 핵심 설계 목표 중 하나는 다양한 운영체제와 컴파일러 환경에서 동작하는 크로스 플랫폼 빌드를 지원하는 것이다. 이를 위해 CMake는 추상화된 명령어 세트를 제공하며, 사용자가 작성한 CMakeLists.txt 파일을 분석하여 각 플랫폼에 맞는 네이티브 빌드 시스템 파일을 생성한다. 예를 들어, 유닉스 계열 시스템에서는 Makefile을, 마이크로소프트 윈도우에서는 비주얼 스튜디오 솔루션 파일(.sln)을, macOS에서는 Xcode 프로젝트를 생성할 수 있다. 이는 개발자가 각 플랫폼의 빌드 도구 세부 사항을 직접 학습하고 관리할 필요 없이, 하나의 설정 파일로 여러 환경을 관리할 수 있게 해준다.
크로스 플랫폼 빌드를 효과적으로 구성하기 위해서는 플랫폼별 차이점을 처리해야 한다. CMake는 이를 위해 시스템 검출 기능과 조건부 구문을 제공한다. CMAKE_SYSTEM_NAME 같은 변수를 통해 현재 대상 플랫폼을 확인하고, if() 명령어를 사용해 운영체제별로 다른 소스 파일, 컴파일러 플래그, 또는 라이브러리 경로를 지정할 수 있다. 또한, 컴파일러의 종류와 버전을 감지하여 최적의 빌드 설정을 적용하는 것도 가능하다.
외부 라이브러리와 도구 체인을 처리하는 것도 크로스 플랫폼 빌드의 중요한 과제이다. CMake는 find_package(), find_library(), find_program() 등의 명령어를 통해 시스템에 설치된 타사 라이브러리를 검색하고 링크 정보를 자동으로 구성한다. 특히 크로스 컴파일 환경을 구성할 때는 툴체인 파일(Toolchain File)을 사용하여 대상 시스템(임베디드 시스템이나 다른 CPU 아키텍처 등)에 맞는 컴파일러, 링커, sysroot 경로 등을 명시적으로 정의한다. 이를 통해 호스트 시스템과는 다른 환경을 위한 빌드를 손쉽게 설정할 수 있다.
5. 고급 기능
5. 고급 기능
5.1. 모듈과 패키지
5.1. 모듈과 패키지
CMake에서 모듈은 빌드 과정에서 자주 사용되는 기능을 캡슐화한 재사용 가능한 스크립트 파일이다. 주로 시스템에 특정 라이브러리나 도구가 존재하는지 검색하는 Find<Package>.cmake 형태로 작성된다. 예를 들어, FindOpenSSL.cmake 모듈은 시스템에 OpenSSL 라이브러리가 설치되어 있는지 경로를 찾고, 그 정보를 CMakeLists.txt에 제공하는 변수로 설정한다. CMake는 자체적으로 많은 유용한 모듈을 내장하고 있으며, 사용자도 프로젝트의 특정 요구사항에 맞게 커스텀 모듈을 작성하여 관리할 수 있다.
패키지는 외부 라이브러리나 의존성을 지칭하며, CMake는 find_package() 명령어를 통해 이러한 패키지를 찾고 프로젝트에 통합한다. 이 명령어는 먼저 CMake의 내장 모듈 디렉토리에서 해당 패키지를 찾는 모듈을 실행한다. 모듈을 통해 패키지를 찾지 못한 경우, CMake는 해당 패키지가 CMake 자체로 빌드 시스템을 제공하는지를 확인하기 위해 패키지명Config.cmake나 패키지명-config.cmake와 같은 패키지 설정 파일을 검색한다.
패키지 설정 파일은 일반적으로 라이브러리 자체에 의해 제공되며, 해당 라이브러리의 헤더 파일 위치, 링크해야 할 라이브러리 파일, 필요한 컴파일러 정의 등을 CMake에 정확히 알려주는 역할을 한다. 이 방식을 통해 SDK나 라이브러리 공급자는 사용자가 find_package() 한 줄로 쉽게 자신들의 제품을 통합할 수 있도록 지원한다. 이는 Makefile이나 비주얼 스튜디오 프로젝트에서 수동으로 경로를 설정하는 번거로움을 크게 줄여준다.
따라서 모듈은 CMake가 외부 패키지를 찾기 위해 사용하는 *도구*라면, 패키지 설정 파일은 패키지 공급자가 CMake와의 통합을 위해 제공하는 *인터페이스*라고 볼 수 있다. 이 두 메커니즘은 크로스 플랫폼 개발 환경에서 복잡한 외부 종속성을 체계적으로 관리하는 CMake의 핵심 강점을 이루는 요소이다.
5.2. 테스트 통합
5.2. 테스트 통합
CMake는 CTest라는 통합된 테스트 도구를 제공하여 소프트웨어 프로젝트의 단위 테스트와 통합 테스트를 자동화하는 기능을 지원한다. 이를 통해 개발자는 빌드 시스템과 동일한 CMakeLists.txt 파일 내에서 테스트 케이스를 정의하고 관리할 수 있다. add_test 명령어를 사용하면 특정 실행 파일이나 스크립트를 테스트로 등록할 수 있으며, 테스트 이름과 실행 명령을 지정한다. 이 방식은 빌드 과정과 테스트 실행을 하나의 워크플로우로 통합하여 CI/CD 파이프라인 구축을 용이하게 한다.
테스트의 세부적인 속성은 set_tests_properties 명령어를 통해 제어할 수 있다. 여기에는 테스트의 타임아웃 설정, 특정 빌드 구성에서만 테스트를 실행하도록 지정하는 작업, 또는 테스트 간의 의존성 관계를 정의하는 작업이 포함된다. 또한 CMake는 gtest나 Catch2와 같은 널리 사용되는 C++ 테스트 프레임워크를 쉽게 통합할 수 있는 모듈을 내장하고 있어, 복잡한 테스트 환경을 간편하게 구성할 수 있다.
테스트를 실행하는 방법은 다양하다. 가장 기본적으로는 빌드 디렉토리에서 ctest 명령을 직접 실행하는 것이다. ctest는 -N 옵션으로 테스트 목록만 확인하거나, -R 옵션으로 정규 표현식에 매칭되는 테스트만 필터링하여 실행하는 등 강력한 제어 기능을 제공한다. 또한 CMake가 생성한 IDE 프로젝트 파일(예: Visual Studio 솔루션)을 통해 통합 개발 환경 내에서 테스트를 실행하고 결과를 확인하는 것도 가능하다.
5.3. 설치 및 패키징
5.3. 설치 및 패키징
CMake는 소프트웨어를 빌드한 후 최종 사용자에게 배포하기 위한 설치 및 패키징 단계를 관리하는 기능을 제공한다. install() 명령어를 사용하면 빌드된 실행 파일, 라이브러리, 헤더 파일, 기타 자원 파일들을 지정된 시스템 디렉토리 구조에 맞게 복사할 위치와 규칙을 정의할 수 있다. 이를 통해 소스 코드에서 빌드 시스템을 거쳐 최종 설치까지의 전체 흐름을 하나의 CMakeLists.txt 파일로 통합 관리할 수 있다.
패키징을 위해서는 CMake에 포함된 CPack 모듈을 활용한다. CPack은 단일 설정 파일에서 다양한 형식의 설치 패키지를 생성할 수 있게 해주는 크로스 플랼폼 도구이다. 프로젝트에 CPack을 포함시키면, make package 또는 비슷한 빌드 타겟을 실행하는 것만으로 여러 형식의 패키지를 한 번에 생성할 수 있다.
CPack이 지원하는 주요 패키지 형식은 다음과 같다.
형식 | 설명 | 주요 사용 플랫폼 |
|---|---|---|
TGZ / ZIP | 압축 아카이브 | 모든 플랫폼 |
DEB | 데비안 패키지 | |
RPM | RPM 패키지 | |
NSIS | 널소프트 설치 프로그램 | |
DragNDrop / Bundle | 드래그 앤 드롭 설치 또는 번들 | |
7Z / TXZ | 기타 압축 형식 | 모든 플랫폼 |
설치 규칙과 패키징 설정은 CMake 캐시 변수를 통해 세부적으로 제어할 수 있으며, CMAKE_INSTALL_PREFIX 같은 변수로 설치 경로를 지정할 수 있다. 이렇게 통합된 설치 및 패키징 시스템은 지속적 통합 및 지속적 배포 파이프라인에 쉽게 통합되어 소프트웨어 배포 과정을 자동화하는 데 핵심 역할을 한다.
6. 관련 도구 및 확장
6. 관련 도구 및 확장
6.1. CPack
6.1. CPack
CPack은 CMake 빌드 시스템에 포함된 크로스 플랫폼 패키징 도구이다. CMake로 구성된 프로젝트를 다양한 형식의 설치 패키지로 만들어 배포하는 역할을 한다. CPack을 사용하면 개발자는 동일한 CMake 스크립트 설정을 기반으로 리눅스, 윈도우, macOS 등 여러 운영체제에 맞는 패키지를 손쉽게 생성할 수 있다. 이는 소프트웨어 배포 과정을 자동화하고 플랫폼 간 일관성을 유지하는 데 크게 기여한다.
CPack은 다양한 패키지 형식을 지원한다. 예를 들어, 리눅스에서는 DEB 패키지나 RPM 패키지를, 윈도우에서는 NSIS 기반의 설치 프로그램이나 ZIP 아카이브를, macOS에서는 번들 또는 DMG 이미지를 생성할 수 있다. 또한 범용적인 형식인 TGZ나 TXZ 같은 압축 아카이브도 만들 수 있어 유연한 배포가 가능하다. 이러한 형식 지원은 CPack의 모듈식 아키텍처 덕분이며, 필요한 패키징 백엔드를 활성화하여 사용한다.
CPack의 사용법은 간단하다. 기본적으로 CMake 프로젝트에 include(CPack) 명령어를 추가하는 것만으로도 기본 설정을 활성화할 수 있다. 더 세부적인 제어가 필요하면 CPACK_ 접두사가 붙은 변수들을 설정하여 패키지 이름, 버전, 공급자, 구성 요소, 설치 경로 등을 상세히 지정할 수 있다. 패키지 생성은 CMake 빌드 디렉토리에서 cpack 명령을 실행하거나, CMake의 package 타겟을 빌드하는 방식으로 이루어진다.
이 도구는 CI/CD 파이프라인과 자연스럽게 통합되어, 빌드 후 자동으로 패키지를 생성하고 배포하는 과정을 구성하는 데 널리 활용된다. CPack을 통해 CMake는 소스 코드 구성, 빌드, 테스트, 패키징에 이르는 전체 개발 워크플로우를 하나의 도구 체인으로 관리하는 통합 솔루션을 제공하게 된다.
6.2. CTest
6.2. CTest
CTest는 CMake 빌드 시스템의 일부로 제공되는 테스트 자동화 도구이다. CMake 프로젝트의 테스트 단계를 관리하고 실행하는 역할을 담당하며, 단위 테스트나 통합 테스트를 포함한 다양한 테스트를 표준화된 방식으로 실행할 수 있게 한다. CTest를 사용하면 개발자는 빌드 시스템 내에서 직접 테스트 케이스를 정의하고, 명령줄에서 간단한 명령어로 모든 테스트를 실행하거나 특정 테스트를 필터링할 수 있다.
CTest의 주요 기능은 CMakeLists.txt 파일 내에 add_test() 명령어를 사용해 테스트를 정의하는 것이다. 이 명령어를 통해 테스트의 이름과 실행할 실행 파일 또는 스크립트를 지정하면, CTest는 이를 인식하고 관리한다. 이후 ctest 명령을 실행하면 프로젝트에 정의된 모든 테스트가 자동으로 실행되며, 결과는 성공, 실패, 통과하지 못함 등의 상태로 보고된다. CTest는 테스트 실행 시 타임아웃 설정, 테스트 간 의존성 관리, 테스트에 특정 프로퍼티를 부여하는 고급 기능도 지원한다.
CTest는 지속적 통합 파이프라인과의 통합에도 유용하다. 테스트 실행 결과를 XML이나 JSON 형식으로 출력하여, 젠킨스나 깃허브 액션과 같은 CI/CD 도구에서 결과를 쉽게 파싱하고 리포트를 생성할 수 있게 한다. 또한, CDash라는 대시보드 서버와 연동하여 테스트 결과, 코드 커버리지, 동적 분석 결과를 중앙에서 수집하고 시각화하는 기능을 제공한다. 이를 통해 분산된 환경에서 진행되는 대규모 프로젝트의 테스트 상태를 효과적으로 모니터링할 수 있다.
6.3. CDash
6.3. CDash
CDash는 CMake 생태계의 일부로, 소프트웨어 프로젝트의 빌드 및 테스트 결과를 수집, 관리, 시각화하는 웹 대시보드 서버이다. 킷웨어에 의해 개발되었으며, 지속적 통합 및 지속적 배포 파이프라인에서 중요한 역할을 한다. CDash는 개발 팀이 여러 플랫폼과 컴파일러에서 수행되는 빌드의 상태, 테스트 결과, 코드 커버리지, 동적 분석 결과 등을 중앙에서 모니터링할 수 있게 해준다.
CDash의 주요 기능은 CTest를 통해 제출된 데이터를 기반으로 한다. 개발자는 프로젝트의 CMakeLists.txt 파일에서 CTest 스크립트를 구성하여, 빌드가 완료된 후 자동으로 테스트를 실행하고 그 결과를 지정된 CDash 서버로 전송하도록 설정할 수 있다. CDash는 이 데이터를 받아 타임라인, 그래프, 요약 보고서 형태로 제공하며, 빌드 실패, 테스트 실패, 경고 증가 등의 문제를 빠르게 식별할 수 있도록 돕는다.
이 도구는 특히 대규모 오픈 소스 프로젝트나 엔터프라이즈 환경에서 유용하다. 여러 개발자가 다양한 환경에서 코드를 변경할 때, CDash 대시보드를 통해 모든 변경 사항이 전체 시스템에 미치는 영향을 종합적으로 확인할 수 있다. 이를 통해 리그레션을 신속하게 발견하고 소프트웨어의 품질을 유지하는 데 기여한다. CDash는 CPack, CTest와 함께 CMake의 강력한 프로젝트 관리 도구군을 완성한다.
7. 여담
7. 여담
CMake는 원래 INSIGHT ToolKit이라는 의료 영상 처리 라이브러리를 위한 크로스 플랫폼 빌드 도구로 개발되었다. 이 특수한 필요성에서 출발했지만, 그 유연성과 강력함 덕분에 C와 C++를 넘어 포트란이나 C# 같은 다른 언어의 프로젝트에서도 널리 채택되며 사실상의 표준 빌드 시스템 도구가 되었다.
CMake의 철학은 "빌드 시스템의 빌드 시스템"에 가깝다. CMake 자체는 Makefile이나 Visual Studio 솔루션 파일을 직접 생성하지 않으며, 대신 이러한 네이티브 빌드 시스템을 생성하는 중간 도구 역할을 한다. 이로 인해 초보자에게는 간접적인 느낌을 줄 수 있지만, 동일한 CMakeLists.txt 파일로 리눅스의 GNU Make, macOS의 Xcode, Windows의 MSBuild 등 완전히 다른 빌드 환경을 지원할 수 있는 강력한 장점으로 작용한다.
CMake 커뮤니티와 생태계는 매우 활발히 성장하고 있다. 수많은 오픈 소스 라이브러리들이 CMake를 빌드 시스템으로 채택함에 따라, 패키지 관리와 의존성 처리를 위한 CMake 패키지나 vcpkg, Conan 같은 외부 도구와의 통합도 중요한 주제가 되었다. 또한, 지속적 통합 파이프라인에서의 사용이 보편화되면서, CTest와 CDash를 활용한 테스트 및 결과 보고 기능도 프로젝트 관리에 필수적인 요소로 자리 잡았다.
