임베디드 Linux 디바이스 구성하기

특정 디바이스에 대해 Qt를 교차 컴파일하려면 툴체인과 시스루트가 필요합니다. 툴체인에는 gcc 또는 다른 컴파일러 버전과 크로스 컴파일을 위해 빌드된 관련 도구가 포함되어 있을 것으로 예상됩니다. 즉, 이러한 도구는 호스트 시스템(일반적으로 x64)에서 실행되는 동시에 대상 아키텍처(예: 32비트 또는 64비트 ARM)용 바이너리를 생성합니다. 시스루트에는 대상 시스템의 헤더와 라이브러리가 포함되어 있어 호스트에서 라이브러리와 애플리케이션을 컴파일하고 연결할 수 있습니다.

이 개요 페이지에서는 Yocto 또는 Buildroot와 같은 배포 빌드 시스템을 사용하지 않는 일반적인 접근 방식에 대해 설명합니다. 적절한 툴체인과 시스루트를 사용할 수 있는 한 항상 Qt를 교차 컴파일하고 장치에 배포할 수 있습니다.

경고: 이 페이지는 일반적인 개괄적인 개요만 제공할 수 있습니다. 빌드 환경, 대상 디바이스 및 툴체인에 따라 달라질 수 있는 세부 사항은 매우 많습니다. 확실하지 않은 경우 시스템 통합업체에 문의하세요. 사전 빌드된 참조 이미지 및 SDK의 경우 Boot to Qt 오퍼링을 참조하세요.

X11이나 Wayland와 같은 윈도우 시스템 없이 Qt 기반 애플리케이션을 실행할 때 일부 디바이스에서는 EGL 및 OpenGL ES 지원을 위한 공급업체별 적응 코드가 필요합니다. 이는 EGLFS 플랫폼 플러그인용 백엔드 형태로 제공됩니다. 이는 소프트웨어 기반 렌더링 전용인 LinuxFB 플랫폼 플러그인을 사용하는 플랫폼과 같은 비가속화 플랫폼에는 해당되지 않습니다. Qt 6부터 많은 임베디드 시스템에서 비디오 모드를 설정하고 디스플레이 커넥터와 그래픽 표면을 관리하기 위해 drm을 사용합니다. 예를 들어 NXP i.MX8 기반 디바이스나 라즈베리 파이 4는 이 방식을 사용하므로 EGLFS에 가장 일반적으로 사용되는 백엔드는 eglfs_kms로, 표면 및 버퍼 관리를 위해 gbm 을 사용하여 EGL 및 OpenGL ES 기반 렌더링( drm)을 가능하게 합니다. NXP i.MX6와 같은 구형 디바이스에서는 eglfs_viv 와 같은 전용 eglfs 백엔드를 사용하여 EGL 창 표면을 프레임버퍼에 연결하는 기존 GPU 공급업체별 방식을 계속 사용할 것입니다.

참고: Qt는 임베디드 디바이스용 소프트웨어 스택의 한 구성 요소에 불과하다는 점에 유의하세요. 특히 가속 그래픽이 관련된 경우, Qt는 디스플레이 드라이버와 같은 사용자 공간과 커널 구성 요소에 적합한 구성을 갖춘 기능적인 그래픽 스택을 기대합니다. 이러한 구성 요소는 Qt의 영역 밖에 있으며, 가속 그래픽을 포함하여 기본 시스템이 완벽하게 작동하고 최적화되도록 하는 것은 시스템 통합자의 책임입니다.

임베디드 리눅스 시스템의 그래픽 및 입력 구성에 대한 자세한 내용은 임베디드 리눅스용 Qt를 참조하십시오.

툴체인 파일과 디바이스 제조 사양 비교

Qt 5에서는 일반적으로 qtbase/mkspecs/devices 디렉터리에서 디바이스 사양을 사용합니다. 여기에는 특정 장치에 적합한 컴파일러 및 링커 플래그가 포함되어 있으며, 시스템 루트의 비표준 위치에 있는 경우 올바른 EGL 및 OpenGL ES 라이브러리가 선택되는지 확인합니다.

예를 들어, 다음과 같은 configure 명령으로 라즈베리 파이 2용 Qt 5 빌드를 구성할 수 있습니다:

./configure -release -opengl es2 -device linux-rasp-pi2-g++ -device-option CROSS_COMPILE=$TOOLCHAIN/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf- -sysroot $ROOTFS -prefix /usr/local/qt5

참고: ninja 실행 파일을 사용할 수 있는 경우 configure는 항상 Ninja 생성기 및 빌드 도구를 사용합니다. Ninja는 크로스 플랫폼으로 기능이 풍부하고 성능이 뛰어나며 모든 플랫폼에서 사용할 것을 권장합니다. 다른 생성기를 사용할 수도 있지만 공식적으로 지원되지는 않습니다.

Qt 6와 CMake에서는 더 이상 이러한 접근 방식만으로는 충분하지 않습니다. 그 대신 CMake 툴체인 파일을 제공해야만 구성이 가능합니다. 이 파일에서 컴파일러 및 링커 플래그, 툴체인 및 시스루트 관련 특이 사항과 관련된 사용자 정의가 이루어집니다.

아래 섹션에서는 최소한의 사용자 지정으로 많은 경우에 사용할 수 있는 툴체인 파일을 소개합니다. 이 블로그 게시물에 제시된 접근 방식을 기반으로 합니다.

참고: 아래에 제시된 툴체인 파일은 예시일 뿐이며, 특정 디바이스에 맞게 추가 사용자 지정이 필요한 경우가 많습니다. 사용자와 시스템 통합자는 원하는 방식으로 자신만의 툴체인 파일을 자유롭게 만들 수 있습니다.

CMake는 Qt 자체 빌드를 위한 유일한 지원 빌드 시스템이지만, Qt 6.0에서는 여전히 qmake 을 사용하여 애플리케이션을 빌드할 수 있습니다. 크로스 컴파일에서 작동하는 qmake 설정을 얻으려면 CMake에 레거시 인수 중 일부를 지정하거나 구성해야 합니다.

호스트 도구

Qt를 교차 컴파일하려면 Qt의 호스트 빌드를 사용할 수 있어야 합니다. 빌드 도중 moc, rcc, qmlcachegen, qsb 등과 같은 도구가 이 곳에서 호출됩니다. 예를 들어 x64 머신에서 ARM용으로 크로스 컴파일하는 경우 동일한 Qt 버전의 로컬 x64 빌드를 먼저 사용할 수 있어야 합니다. 이 Qt 빌드의 경로는 configure 또는 cmake에 전달됩니다.

Qt 구성하기

다음을 사용할 수 있다고 가정해 보겠습니다:

  • $HOME/rpi-sdk 에 있는 툴체인과 시스루트,
  • $HOME/qt-cross 에 Qt 체크 아웃, 최소한 qtbase 모듈이 있습니다,
  • $HOME/qt-host 에 있는 Qt의 호스트 빌드.

또한 구성하기 전에 다음 사항을 결정해야 합니다:

  • 빌드가 완료되면 로컬 시스템에서 Qt 빌드를 어디에 설치할 것인가? 이 예제에서는 $HOME/qt6-rpi 을 사용합니다.
  • Qt 빌드를 디바이스의 어디에 배포할 것인가? 이 예제에서는 /usr/local/qt6 을 사용합니다.

이 예제에서는 Yocto를 통해 생성된 라즈베리파이 4 SDK(툴체인+시스템루트)를 사용하지만, 여기서의 지침은 완전히 일반적이며 Yocto에 대한 의존성이 없습니다. 도구체인 파일이 올바른 크로스 컴파일러 및 기타 경로로 업데이트되면 다른 도구체인 및 시스루트에서도 단계는 동일합니다.

build 디렉터리를 생성하고 전환한 후:

$HOME/qt-cross/qtbase/configure -release -opengl es2 -nomake examples -nomake tests \
  -qt-host-path $HOME/qt-host                              \
  -extprefix $HOME/qt6-rpi                                 \
  -prefix /usr/local/qt6                                   \
  -- -DCMAKE_TOOLCHAIN_FILE=$HOME/qt-cross/toolchain.cmake

실제로 이 configure 명령은 다음과 같은 직접 CMake 호출과 동일합니다:

cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DINPUT_opengl=es2 -DQT_BUILD_EXAMPLES=OFF -DQT_BUILD_TESTS=OFF \
  -DQT_HOST_PATH=$HOME/qt-host                           \
  -DCMAKE_STAGING_PREFIX=$HOME/qt6-rpi                   \
  -DCMAKE_INSTALL_PREFIX=/usr/local/qt6                  \
  -DCMAKE_TOOLCHAIN_FILE=$HOME/qt-cross/toolchain.cmake  \
  $HOME/qt-cross/qtbase

적절한 툴체인 파일이 주어지면, 이 정도면 Qt 빌드를 생성한 다음 CMake를 사용하여 애플리케이션을 빌드할 수 있습니다. qmake 로도 애플리케이션을 빌드할 수 있도록 하려면 위에 표시된 모든 인수와 함께 Qt 5 스타일의 디바이스 사양 및 디바이스 옵션을 지정해야 합니다:

$HOME/qt-cross/qtbase/configure ...
  ...
  -device linux-rasp-pi4-v3d-g++ \
  -device-option CROSS_COMPILE=$HOME/rpi_sdk/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi- \
  -device-option DISTRO_OPTS="hard-float" \
  ...

기본적으로 크로스 컴파일 시에는 대상 기기에서 실행되어야 하는 Qt 라이브러리와 도구만 빌드됩니다. mocuic 같은 빌드 관련 도구는 빌드되지 않습니다. QT_FORCE_BUILD_TOOLSON 으로 설정하면 이러한 도구 빌드를 활성화할 수 있습니다.

참고: QT_FORCE_BUILD_TOOLS 을 활성화하면 qmake 과 같은 도구의 타겟 바이너리가 스테이징 위치에 설치됩니다. 따라서 qmake 을 사용하여 애플리케이션을 빌드하는 경우 host-qmake 스크립트를 대신 호출하세요.

구성이 오류 없이 완료되면 cmake --build . --parallel 을 실행하여 빌드합니다. 빌드가 완료되면 cmake --install . 을 실행하여 $HOME/qt6-rpi 에 결과를 설치합니다. 거기에서 rsync, scp 또는 다른 방법을 사용하여 Qt 빌드를 장치에 배포할 수 있습니다.

개별 Qt 모듈을 빌드하는 경우, 스테이징 위치의 bin 디렉토리(예제에서는$HOME/qt6-rpi )에서 qt-configure-module 스크립트를 사용하여 qtdeclarative, qtquick3d 등과 같은 추가 모듈을 구성할 수 있습니다. 그런 다음 cmake --build . 을 사용하여 빌드하고 다음을 실행하여 스테이징 위치에 설치할 수 있습니다. cmake --install .

참고: 빌드를 시작하기 전에 항상 구성 단계의 출력을 주의 깊게 검사하세요. 예상되는 모든 기능이 활성화되어 있나요? 구성 시 필수 기능이 활성화되어 있지 않으면 빌드를 만들어서 디바이스에 배포해도 소용이 없습니다.

예를 들어 OpenGL을 통한 가속 그래픽이 필요한 경우 다음 기능에 특히 주의를 기울여야 합니다:

EGL .................................... yes
OpenGL:
  Desktop OpenGL ....................... no
  OpenGL ES 2.0 ........................ yes
  OpenGL ES 3.0 ........................ yes
...
evdev .................................. yes
libinput ............................... yes
...
EGLFS .................................. yes
EGLFS details:
  EGLFS OpenWFD ........................ no
  EGLFS i.Mx6 .......................... no
  EGLFS i.Mx6 Wayland .................. no
  EGLFS RCAR ........................... no
  EGLFS EGLDevice ...................... yes
  EGLFS GBM ............................ yes
  EGLFS VSP2 ........................... no
  EGLFS Mali ........................... no
  EGLFS Raspberry Pi ................... no
  EGLFS X11 ............................ no
LinuxFB ................................ yes

라즈베리 파이 4 예제에서는 EGL, OpenGL ES 및 EGLFS GBM 이 모두 yes 으로 보고되어야 하며, 그렇지 않으면 EGLFS 플랫폼 플러그인 및 해당 eglfs_kms 백엔드가 장치에서 작동하지 않습니다. 마우스, 키보드, 터치 입력 기능을 사용하려면 evdev 또는 libinput 을 활성화해야 합니다.

마찬가지로 X11을 디바이스에서 윈도우 시스템으로(또는 그 중 하나로) 사용할 계획이라면 xcb 및 X11 관련 기능이 yes 로 표시되어 있는지 확인하세요.

툴체인 파일 예시

$HOME/rpi-sdk 에 시스루트와 툴체인이 있다고 가정합니다. TARGET_SYSROOTCROSS_COMPILER 는 사용 중인 툴체인과 시스루트에 맞게 조정해야 합니다. 여기 예제는 Yocto에서 생성한 특정 SDK 하나에만 적합합니다. CMAKE_C_COMPILERCMAKE_CXX_COMPILER 도 마찬가지입니다.

PKG_CONFIG_*와 같은 환경 변수를 제공하는 래퍼 스크립트에는 의존하지 않습니다. 대신 .pc 파일의 경로가 툴체인 파일에 지정됩니다. 다른 시스루트는 PKG_CONFIG_LIBDIR 에서 조정이 필요할 수 있습니다. 예를 들어, 라즈베리 파이 OS(이전의 라즈비안) 이미지에서 생성된 시스루트의 경우 /usr/lib/arm-gnueabihf/pkgconfig 대신 을 사용합니다.

컴파일러 및 링커 플래그는 이 예제에서 최적으로 설정할 필요는 없습니다. 대상 디바이스에 맞게 필요에 따라 조정하세요.

예제 툴체인 파일의 CMake 세부 사항에 대한 자세한 내용은 이 블로그 게시물과 CMake 설명서를 참조하세요.

cmake_minimum_required(VERSION 3.18)
include_guard(GLOBAL)

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(TARGET_SYSROOT /home/user/rpi-sdk/sysroots/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi)
set(CROSS_COMPILER /home/user/rpi-sdk/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi)

set(CMAKE_SYSROOT ${TARGET_SYSROOT})

set(ENV{PKG_CONFIG_PATH} "")
set(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_SYSROOT}/usr/lib/pkgconfig:${CMAKE_SYSROOT}/usr/share/pkgconfig)
set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT})

set(CMAKE_C_COMPILER ${CROSS_COMPILER}/arm-poky-linux-gnueabi-gcc)
set(CMAKE_CXX_COMPILER ${CROSS_COMPILER}/arm-poky-linux-gnueabi-g++)

set(QT_COMPILER_FLAGS "-march=armv7-a -mfpu=neon -mfloat-abi=hard")
set(QT_COMPILER_FLAGS_RELEASE "-O2 -pipe")
set(QT_LINKER_FLAGS "-Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed")

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

include(CMakeInitializeConfigs)

function(cmake_initialize_per_config_variable _PREFIX _DOCSTRING)
  if (_PREFIX MATCHES "CMAKE_(C|CXX|ASM)_FLAGS")
    set(CMAKE_${CMAKE_MATCH_1}_FLAGS_INIT "${QT_COMPILER_FLAGS}")

    foreach (config DEBUG RELEASE MINSIZEREL RELWITHDEBINFO)
      if (DEFINED QT_COMPILER_FLAGS_${config})
        set(CMAKE_${CMAKE_MATCH_1}_FLAGS_${config}_INIT "${QT_COMPILER_FLAGS_${config}}")
      endif()
    endforeach()
  endif()

  if (_PREFIX MATCHES "CMAKE_(SHARED|MODULE|EXE)_LINKER_FLAGS")
    foreach (config SHARED MODULE EXE)
      set(CMAKE_${config}_LINKER_FLAGS_INIT "${QT_LINKER_FLAGS}")
    endforeach()
  endif()

  _cmake_initialize_per_config_variable(${ARGV})
endfunction()

대상 디바이스용 애플리케이션 빌드하기

Qt 빌드가 완료되고 스테이징 위치에 설치되면 예제 또는 애플리케이션을 빌드할 수 있습니다.

CMake를 사용하여 스테이징 위치의 bin 디렉토리(예제에서는$HOME/qt6-rpi )에서 생성된 qt-cmake 스크립트를 사용하여 구성한 다음 ninja 을 실행합니다. 예를 들어

$HOME/qt6-rpi/bin/qt-cmake .
cmake --build .

그러면 결과 애플리케이션 바이너리를 디바이스에 배포할 수 있습니다. qt-cmake 헬퍼 스크립트를 사용하면 Qt 빌드에 사용된 툴체인 파일이 로드되므로 각 애플리케이션에 대해 반복적으로 지정할 필요가 없으므로 편리합니다.

Qt 자체의 경우와 달리, 적절한 디바이스 사양을 사용할 수 있고 Qt를 구성할 때 적절한 레거시 인수를 CMake 또는 configure에 전달한 경우라면 Qt 6.0에서도 qmake로 애플리케이션을 빌드하는 것이 여전히 지원됩니다. 이 모든 것이 사실이라면 qmakemake 을 실행하면 대상 디바이스에 대한 애플리케이션 바이너리도 생성됩니다.

플랫폼 플러그인 및 EGLFS의 기본값

구성이 완료되면 기본 플랫폼 플러그인이 선택됩니다. 이는 -platform 인수를 사용하지 않고 QT_QPA_PLATFORM 환경 변수를 설정하지 않은 상태에서 애플리케이션을 시작할 때 사용됩니다.

마찬가지로 EGLFS 플랫폼 플러그인에는 여러 백엔드가 있습니다. 기본값은 가용성과 사전 정의된 우선 순위에 따라 선택됩니다. drm 및 gbm을 사용할 수 있는 경우 기본값은 eglfs_kms 백엔드가 됩니다. 이 기본값은 런타임에 QT_QPA_EGLFS_INTEGRATION 환경 변수를 설정하여 언제든지 재정의할 수 있습니다.

런타임에 특정 값을 강제하지 않고 빌드에 대한 이러한 기본값을 변경하려면 CMake가 한 번 실행된 후 다음 두 가지 CMake 캐시 변수를 사용할 수 있습니다:

  • QT_QPA_DEFAULT_PLATFORM (STRING) - 기본 플랫폼 플러그인의 이름입니다.
  • QT_QPA_DEFAULT_EGLFS_INTEGRATION (STRING) - 기본 EGLFS 백엔드.

이러한 변수는 툴체인 파일 내부에서도 설정할 수 있습니다.

Qt 구성에 대한 자세한 내용은 Qt 구성 옵션을 참조하십시오.

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.