[모던 CMake] 크로스 컴파일과 툴체인 파일 활용

모던 CMake를 활용하여 효율적인 C++ 프로젝트 빌드 시스템을 구축하는 방법을 계속해서 알아보겠습니다. 이번 글에서는 크로스 컴파일 환경에서 CMake를 사용하는 방법과 툴체인 파일(toolchain file)을 활용하여 다양한 플랫폼용 빌드 설정을 관리하는 방법에 대해 다루겠습니다.

크로스 컴파일이란?

크로스 컴파일은 현재 사용 중인 호스트 시스템과 다른 플랫폼(타겟 시스템)용으로 코드를 컴파일하는 것을 의미합니다. 예를 들어, x86 기반 PC에서 ARM 기반 임베디드 시스템용 바이너리를 빌드하는 경우가 이에 해당합니다.

CMake에서의 크로스 컴파일 설정

CMake는 기본적으로 호스트 시스템용으로 빌드를 수행하지만, 툴체인 파일을 통해 크로스 컴파일 환경을 설정할 수 있습니다.

툴체인 파일의 역할

툴체인 파일은 컴파일러, 링커, 라이브러리 경로 등 타겟 시스템에 대한 정보를 지정하는 CMake 스크립트입니다. 이를 통해 CMake는 호스트가 아닌 타겟 시스템에 맞는 빌드 설정을 적용할 수 있습니다.

툴체인 파일 작성하기

예제: ARM 임베디드 시스템용 툴체인 파일

arm-toolchain.cmake

# 타겟 시스템 지정
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

# 컴파일러 경로 설정
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)

# sysroot 설정 (필요한 경우)
set(CMAKE_SYSROOT /path/to/arm-sysroot)

# 추가 설정 (필요한 경우)
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
  • CMAKE_SYSTEM_NAME: 타겟 시스템의 운영 체제 이름을 지정합니다.
  • CMAKE_SYSTEM_PROCESSOR: 타겟 시스템의 프로세서 아키텍처를 지정합니다.
  • CMAKE_C_COMPILER, CMAKE_CXX_COMPILER: 타겟용 컴파일러를 지정합니다.
  • CMAKE_SYSROOT: 타겟 시스템의 루트 파일 시스템 경로를 지정합니다.

프로젝트 구성 파일에서의 설정

CMakeLists.txt에서 별도의 설정은 필요 없으며, 기존 설정을 그대로 유지합니다.

cmake_minimum_required(VERSION 3.15)
project(MyProject)

add_executable(MyApp src/main.cpp)

크로스 컴파일 빌드 수행

툴체인 파일을 사용하여 CMake를 호출합니다.

cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=path/to/arm-toolchain.cmake
cmake --build build

여러 플랫폼 지원을 위한 설정

프로젝트에서 여러 플랫폼을 지원해야 하는 경우, 각각의 플랫폼에 대한 툴체인 파일을 작성하고, 빌드 스크립트를 통해 선택적으로 사용할 수 있습니다.

예제: x86_64와 ARM 플랫폼 지원

  1. x86_64용 툴체인 파일 (x86_64-toolchain.cmake)
  2. # 호스트 시스템용이므로 특별한 설정이 필요 없을 수 있습니다. # 필요에 따라 컴파일러 경로 등을 지정합니다.
  3. ARM용 툴체인 파일 (arm-toolchain.cmake)
  4. 앞서 작성한 arm-toolchain.cmake를 사용합니다.
  5. 빌드 스크립트 작성
  6. #!/bin/bash # x86_64 빌드 cmake -B build/x86_64 -S . -DCMAKE_TOOLCHAIN_FILE=path/to/x86_64-toolchain.cmake cmake --build build/x86_64 # ARM 빌드 cmake -B build/arm -S . -DCMAKE_TOOLCHAIN_FILE=path/to/arm-toolchain.cmake cmake --build build/arm

CMake 프리셋(Presets) 활용

CMake 3.19 버전부터는 CMakePresets.json 파일을 통해 빌드 설정을 관리할 수 있습니다. 이를 통해 다양한 빌드 구성을 쉽게 정의하고 사용할 수 있습니다.

CMakePresets.json 예제

{
  "version": 3,
  "configurePresets": [
    {
      "name": "x86_64-release",
      "displayName": "x86_64 Release",
      "description": "Release build for x86_64",
      "generator": "Unix Makefiles",
      "binaryDir": "${sourceDir}/build/x86_64-release",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release"
      }
    },
    {
      "name": "arm-debug",
      "displayName": "ARM Debug",
      "description": "Debug build for ARM",
      "generator": "Unix Makefiles",
      "binaryDir": "${sourceDir}/build/arm-debug",
      "toolchainFile": "path/to/arm-toolchain.cmake",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug"
      }
    }
  ],
  "buildPresets": [
    {
      "name": "x86_64-release",
      "configurePreset": "x86_64-release"
    },
    {
      "name": "arm-debug",
      "configurePreset": "arm-debug"
    }
  ]
}

프리셋을 사용한 빌드

cmake --preset x86_64-release
cmake --build --preset x86_64-release

cmake --preset arm-debug
cmake --build --preset arm-debug
  • 프리셋을 사용하면 빌드 설정을 명령줄이 아닌 파일로 관리할 수 있어 편리합니다.
  • 팀 내에서 동일한 빌드 구성을 공유하기에도 용이합니다.

외부 라이브러리의 크로스 컴파일

크로스 컴파일 시 외부 라이브러리의 경로나 빌드 방식이 달라질 수 있습니다. 이를 위해 find_package()나 find_library()를 사용할 때 타겟 시스템의 경로를 지정해야 합니다.

예제: 타겟 전용 라이브러리 찾기

# 툴체인 파일에서 CMAKE_FIND_ROOT_PATH 설정이 중요합니다.

find_library(MYLIB_LIB mylib PATHS /target/sysroot/lib)

target_link_libraries(MyApp PRIVATE ${MYLIB_LIB})
  • CMAKE_FIND_ROOT_PATH를 통해 타겟 시스템의 경로를 지정하면, CMake는 해당 경로에서 라이브러리를 찾습니다.

크로스 컴파일 디버깅

크로스 컴파일된 바이너리를 디버깅하려면 타겟 시스템에서 디버거를 실행하거나, GDB 서버 등을 활용하여 원격 디버깅을 수행해야 합니다.

GDB 서버를 사용한 원격 디버깅 예제

  1. 타겟 시스템에서 GDB 서버 실행
  2. gdbserver :1234 ./MyApp
  3. 호스트 시스템에서 GDB 실행
  4. arm-linux-gnueabihf-gdb ./MyApp
  5. GDB에서 원격 접속
  6. (gdb) target remote target-ip:1234
  • 이렇게 하면 호스트 시스템에서 타겟 시스템의 실행 파일을 디버깅할 수 있습니다.

CMake와 여러 언어의 크로스 컴파일

CMake는 C++ 외에도 C, Fortran, CUDA 등의 언어를 지원하며, 크로스 컴파일 시에도 동일한 방식으로 설정할 수 있습니다.

예제: CUDA 프로젝트의 크로스 컴파일

CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(MyCUDAProject LANGUAGES CXX CUDA)

add_executable(MyApp src/main.cu)

# 필요한 CUDA 컴파일 옵션 설정
  • 툴체인 파일에서 CMAKE_CUDA_COMPILER 등을 설정하여 타겟용 CUDA 컴파일러를 지정합니다.

결론

이번 글에서는 크로스 컴파일 환경에서 CMake를 사용하는 방법과 툴체인 파일을 활용하여 다양한 플랫폼용 빌드 설정을 관리하는 방법에 대해 알아보았습니다. 또한, CMake 프리셋을 통해 빌드 구성을 효율적으로 관리하는 방법도 살펴보았습니다.

반응형