CMake Advanced

Date:     Updated:

카테고리:

인프런에 있는 삼각형님의 삼각형의 실전! CMake 초급 강의를 듣고 정리한 내용입니다.


Modern Build System

어떤 프로젝트를 처음부터 설계하고 구현한다고 생각해보자. 당연히 처음에는 이상적인 구조로 설계하고 구현할 것이다. 하지만 점점 기존 설계에 맞지 않는 기능 추가 요청이 들어오며 구조가 망가지기 시작한다. 타겟 간의 역할 구분도 희미해지고, 상호 참조가 발생하는 최악의 구조로도 갈 수 있다. 만약 타겟 간의 상호 참조가 일어난다면, 코드 한 줄을 수정했을 때 프로젝트 여기저기서 버그가 발생할 것이다.
이런 문제를 방지하기 위해, CMake가 기능은 제공하지만 지양해야 하는 설정들이 존재한다.


# 모든 타겟에 영향을 주는 인클루드 디렉토리를 지정 
include_directories(include)

# 모든 타겟에 영향을 주는 컴파일 디파인을 지정 
add_definitions(NOMINMAX)

# 모든 타겟이 링킹할 타겟이나 라이브러리를 지정 
link_libraries(abc xyz)

# ABI에 영향을 주는 컴파일 옵션을 타겟에 지정 
target_compile_options(foo PRIVATE -no-rtti -std=c++0x)

# 타겟 밖의 디렉토리를 타겟의 인클루드 디렉토리로 지정 
target_include_directories(foo PUBLIC ../bar/include)


Vcpkg를 이용한 의존성 관리

vcpkg는 오픈소스인 C++ 패키지 매니저이다. vcpkg로 설치한 패키지는 CMake의 find_packages 커맨드로 찾아 프로젝트에서 사용할 수 있다. 단 CMake가 vcpkg를 사용한다는 것을 알도록 해야하는데, 두 가지 방법이 존재한다.

  • 툴체인 옵션으로 vcpkg 경로 지정
❯ cmake . -B build --toolchain=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake


  • D 옵션으로 CMAKE_TOOLCHAIN_FILE 내장 변수 값 입력
❯ cmake . -B build -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake


FetchContent를 이용한 의존성 관리

CMake에서 제공하는 FetchContent 모듈을 사용하면 GIT, URL, SVN등 다양한 방법으로 패키지를 받아와 의존성을 관리할 수 있다.

# 모듈 로드 
include(FetchContent)

# 컨텐츠를 Git에서 가져온다고 선언 
FetchContent_Declare(spdlog
   GIT_REPOSITORY https://github.com/gabime/spdlog.git
   GIT_TAG 706ad7059125f32158dad4441938c08fa910f143)

# 컨텐츠를 URL 다운로드를 통해 가져온다고 선언 
FetchContent_Declare(poco
   URL https://github.com/pocoproject/poco/archive/refs/tags/poco-1.12.4-release.tar.gz
   URL_HASH MD5=0ca5d1e2f2a5e8ba2f0a83c2e6df374a
   DOWNLOAD_EXTRACT_TIMESTAMP 20230316091300  # YYYYMMDDHHMMSS
)

# 선언된 컨텐츠들(spdlog, poco)을 프로젝트에서 사용할 수 있도록 준비 
FetchContent_MakeAvailable(spdlog poco)

# 타겟 프로그램 정의
add_executable(fetch-content src/main.cpp)

# 타겟을 링킹할 때 필요한 타겟이나 라이브러리를 지정 
target_link_libraries(fetch-content
PRIVATE
    spdlog::spdlog
    Poco::Net
)
  • 참고로 파일의 MD5 해시 값을 구할 때에는
    • macOS : coreutils 패키지의 md5sum 이용
    • windows : CertUtil 패키지 이용


CMake 테스트

CMake는 CTest라는 테스트 도구 모듈을 지원한다. CTest 모듈을 이용하면 필요한 테스트들을 쉽게 정의하고 테스트할 수 있다.


# CTest 모듈 로드 
include(CTest)

# 테스트 정의
# foo-test라는 테스트는 pass라는 실행 파일 실행
# pass 파일의 반환 값에 따라 테스트 성공/실패
add_test(NAME foo-test COMMAND pass)

# 테스트 정의
add_test(NAME bar-test COMMAND fail)

# 테스트 정의
add_test(NAME baz-test COMMAND fail "Fail is not fail.")

# 테스트 속성 정의 
# baz_test는 출력이 정규 표현식 "Fail is not fail."과 일치하면 통과
set_test_properties(baz-test
    PROPERTIES PASS_REGULAR_EXPRESSION "Fail is not fail."
)

# 테스트 활성화 
# 현재 경로 이하에 대한 모든 테스트 활성화
enable_testing()


스크린샷 2024-06-15 오후 4 29 54

  • ctest 명령어로 테스트 실행


CMake 설치

CMake는 빌드한 타겟을 특정 위치에 설치하는 작업까지 지원해준다.

# GNUInstallDirs 모듈 로드 
include(GNUInstallDirs)

# 메세지 출력
message(STATUS "BINDIR: ${CMAKE_INSTALL_BINDIR}") 
message(STATUS "LIBDIR: ${CMAKE_INSTALL_LIBDIR}") 
message(STATUS "INCLUDEDIR: ${CMAKE_INSTALL_INCLUDEDIR}") message(STATUS "DATAROOTDIR: ${CMAKE_INSTALL_DATAROOTDIR}")
  • 먼저 GNUInstallDirs 모듈을 로드하면, GNU 설치 방식에 맞는 경로들이 CMAKE_INSTALL_XXXX 변수로 정의된다.
  • 이후 DESTINATION DIR에 사용


install(TARGETS <targets> [EXPORT <export-name>]
        [ARCHIVE | LIBRARY | RUNTIME | FRAMEWORK | BUNDLE]
        [DESTINATION <dir>]
        [PERMISSIONS <permissions>]
        [CONFIGURATIONS [Debug|Release|...]]
        [COMPONENT <component>]
        [NAMESPACE <name>]
        ...)
  • TARGETS : 설치할 타겟(예: 실행 파일, 라이브러리)을 지정
  • EXPORT : 나중에 설치한 파일들을 참조하기 위해 필요한 config 파일
  • DESTINATION : 타겟을 설치할 디렉토리를 지정
  • ARCHIVE, LIBRARY, RUNTIME : 각각 정적 라이브러리, 동적 라이브러리, 실행 파일을 지정
  • PERMISSIONS : 파일 권한을 설정
  • CONFIGURATIONS : 특정 빌드 구성(예: Debug, Release)에 대해 설치를 설정
  • NAMESPACE : 설치되는 대상들을 그룹화하고, 이름 충돌 방지 (필요한 경우 사용)


CMake 배포

CMake는 배포에 용이하게 패키징 작업 또한 지원해준다.

# InstallRequiredSystemLibraries 모듈 로드 
include(InstallRequiredSystemLibraries)

먼저 install한 설치 파일들이 제대로 실행되기 위해서는 프로그램이 사용하고 있는 시스템 런타임 라이브러리가 함께 설치되어야 한다. CMake의 InstallRequiredSystemLibraries 모듈은 필요한 시스템 런타임 라이브러리들을 자동으로 찾아 패키징에 추가해준다.


# 패키징 관련된 변수 설정
set(CPACK_PACKAGE_VENDOR "Triangle")
set(CPACK_PACKAGE_VERSION_MAJOR "0")
set(CPACK_PACKAGE_VERSION_MINOR "1")
set(CPACK_PACKAGE_VERSION_PATCH "0")
set(CPACK_GENERATOR "STGZ;ZIP")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")

# CPack 모듈 로드 
include(CPack)

다음으로 CPack 모듈은 설치 프로그램과 소스 패키지를 생성하는데 필요한 CPackConfig.cmake, CPackSourceConfig.cmake 파일을 생성해준다. 이때 주의할 점은 CPack 모듈을 로드하기 전에, 이에 필요한 변수들을 미리 정의해주어야 한다.


스크린샷 2024-06-15 오후 5 17 20


GLOB 사용 금지

GLOB을 사용하면 폴더 디렉토리 구조를 아주 간단하게 list로 가져올 수 있다. 매우 편리한 기능이지만, GLOB을 이용하여 폴더 구조를 가져오면 CMakeList.txt 파일에서 디렉토리의 변경 사항을 알아차릴 수 없다. 이로 인한 문제가 자주 발생한다고 하니 사용을 지양하자.



맨 위로 이동하기

CMake 카테고리 내 다른 글 보러가기

댓글 남기기