Jump to content

RaspberryPiWithQt6WebEngine: Difference between revisions

From Qt Wiki
Michal Klocek (talk | contribs)
m add more changes bookworm -> trixie
Michal Klocek (talk | contribs)
m typo
 
(7 intermediate revisions by the same user not shown)
Line 19: Line 19:
One Qt6 host build, which is later used to cross-compile the arm64 Qt build.
One Qt6 host build, which is later used to cross-compile the arm64 Qt build.


In this guide we are going to cross compile Qt 6.9.
In this guide we are going to cross compile Qt 6.12 with QtWebEngine [https://www.qt.io/blog/qt-webengine-will-release-separate 6.140].


{{note | As shown [[Cross-Compile_Qt_6_for_Raspberry_Pi | here]], we could do a 'top level build' of Qt6, but we are going to do 'prefix module build' just to show an alternative way of building Qt6.}}
{{note | As shown [[Cross-Compile_Qt_6_for_Raspberry_Pi | here]], we could do a 'top level build' of Qt6, but we are going to do 'prefix module build' just to show an alternative way of building Qt6.}}
Line 33: Line 33:
</syntaxhighlight>
</syntaxhighlight>


* Let's checkout the main qt6 repository, select 6.9 branch and fetch source code of minimal subset of qt modules to be able to compile QtWebEngine:<syntaxhighlight lang="bash">
* Let's checkout the main qt6 repository, select 6.12 branch and fetch source code of minimal subset of qt modules to be able to compile QtWebEngine:<syntaxhighlight lang="bash">
cd ~/workspaces/rpi/toolchain/qt
cd ~/workspaces/rpi/toolchain/qt
git clone git://code.qt.io/qt/qt5.git qt6
git clone git://code.qt.io/qt/qt5.git qt6
cd qt6
cd qt6
git checkout origin/6.9
git checkout origin/6.12
./init-repository --module-subset=qtbase,qtshadertools,qtdeclarative,qtwayland,qtwebengine  --no-resolve-deps
./init-repository --module-subset=qtbase,qtshadertools,qtdeclarative --no-resolve-deps
</syntaxhighlight>
</syntaxhighlight>


{{note|Re-selecting other branches requires rerun of init-repository script with option '-f'.}}
{{note|Re-selecting other branches requires rerun of init-repository script with option '-f'.}}
* Let's now clone qtwebenigne repository and switch to 6.140 branch :<syntaxhighlight lang="bash">
cd ~/workspaces/rpi/toolchain/qt
git clone git://code.qt.io/qt/qtwebengine.git qtwebengine
git checkout origin/6.140
git submodule update --init
</syntaxhighlight>


==Sysroot==
==Sysroot==
Line 167: Line 174:
</syntaxhighlight>
</syntaxhighlight>


* Let's configure host build for Qt6 qtbase, build it and install into ~/workspaces/rpi/toolchain/opt/qt/host:<syntaxhighlight lang="bash">
* Let's configure host build for Qt6, build it and install into ~/workspaces/rpi/toolchain/opt/qt/host:<syntaxhighlight lang="bash">
cd /qt/build/host
cd /qt/build/host
mkdir qtbase
mkdir qtbase
cd qtbase  
cd qtbase  
/qt/qt6/qtbase/configure -release -nomake tests -nomake examples -linker mold -prefix /opt/qt/host
/qt/qt6/configure -release -nomake tests -nomake examples -linker mold -prefix /opt/qt/host
cmake --build . --parallel
cmake --build . --parallel
cmake --install .
cmake --install .
Line 178: Line 185:
{{note | The path /qt/qt6/qtbase in chroot is really ~/workspaces/rpi/toolchain/qt/qt6/qtbase on the build machine.}}
{{note | The path /qt/qt6/qtbase in chroot is really ~/workspaces/rpi/toolchain/qt/qt6/qtbase on the build machine.}}


* Let's configure and build remaining modules:<syntaxhighlight lang="bash">
* To be able to cross compile QtWebEngine module we also need gn, therfreo let's configure and build one from qtwebengine:<syntaxhighlight lang="bash">
 
cd /qt/build/host
cd /qt/build/host
mkdir qtshadertools qtdeclarative qtwebengine qtwayland
mkdir qtwebengine
 
cd qtshadertools
/opt/qt/host/bin/qt-configure-module ../../../qt6/qtshadertools
cmake --build . --parallel
cmake --install .
cd ..
 
cd qtdeclarative
/opt/qt/host/bin/qt-configure-module ../../../qt6/qtdeclarative
cmake --build . --parallel
cmake --install .
cd ..
 
cd qtwayland
/opt/qt/host/bin/qt-configure-module ../../../qt6/qtwayland
cmake --build . --parallel
cmake --install .
cd ..
 
cd qtwebengine
cd qtwebengine
/opt/qt/host/bin/qt-configure-module ../../../qt6/qtwebengine
/opt/qt/host/bin/qt-configure-module ../../../qt6/qtwebengine -- -DBUILD_ONLY_GN=ON
cmake --build . --parallel
cmake --build . --parallel
cmake --install .
cmake --install .
cd ..
cd ..
</syntaxhighlight>
</syntaxhighlight>
{{note | We could build the whole module, but we just need gn binary, therfore we use BUILD_ONLY_GN define.}}


==Qt6 Target Cross Build==
==Qt6 Target Cross Build==
Line 223: Line 214:


set(CMAKE_SYSROOT /sysroot)
set(CMAKE_SYSROOT /sysroot)
set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc-12)
set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc-14)
set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++-12)
set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++-14)


set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
Line 269: Line 260:
mkdir qtbase
mkdir qtbase
cd qtbase  
cd qtbase  
../../../qt6/qtbase/configure -release -opengl es2 -nomake examples -nomake tests -qt-host-path /opt/qt/host -extprefix /opt/qt/target -prefix /usr/local/qt6 -- -DCMAKE_TOOLCHAIN_FILE=/qt/rpi.toolchain.cmake
/qt/qt6/configure -release -opengl es2 -nomake examples -nomake tests -qt-host-path /opt/qt/host -extprefix /opt/qt/target -prefix /usr/local/qt6 -- -DCMAKE_TOOLCHAIN_FILE=/qt/rpi.toolchain.cmake
cmake --build . --parallel
cmake --build . --parallel
cmake --install .
cmake --install .
</syntaxhighlight>
</syntaxhighlight>
* Let's configure and build remaining modules:<syntaxhighlight lang="bash">
* Let's configure and build QtWebEngine module:<syntaxhighlight lang="bash">
cd /qt/build/target
cd /qt/build/target
mkdir qtshadertools qtdeclarative qtwebengine qtwayland
mkdir qtwebengine
 
cd qtshadertools
/opt/qt/target/bin/qt-configure-module ../../../qt6/qtshadertools
cmake --build . --parallel
cmake --install .
cd ..
 
cd qtdeclarative
/opt/qt/target/bin/qt-configure-module ../../../qt6/qtdeclarative
cmake --build . --parallel
cmake --install .
cd ..
 
cd qtwayland
/opt/qt/host/bin/qt-configure-module ../../../qt6/qtwayland
cmake --build . --parallel
cmake --install .
cd ..


cd qtwebengine
cd qtwebengine
Line 316: Line 289:
cd simplebrowser
cd simplebrowser


/opt/qt/target/bin/qt-cmake ../../../qt6/qtwebengine/examples/webenginewidgets/simplebrowser/CMakeLists.txt -DCMAKE_INSTALL_PREFIX=/opt/qt/target/ -G Ninja
/opt/qt/target/bin/qt-cmake ../../../qt6/qtwebengine/examples/webenginewidgets/simplebrowser -DCMAKE_INSTALL_PREFIX=/opt/qt/target/ -G Ninja
cmake --build .
cmake --build .
cmake --install .</syntaxhighlight>
cmake --install .</syntaxhighlight>
Line 348: Line 321:
It is time to copy the image to the SD card (replace sdX with the device on your machine):<syntaxhighlight lang="bash">
It is time to copy the image to the SD card (replace sdX with the device on your machine):<syntaxhighlight lang="bash">
cd ~/workspaces/rpi/image
cd ~/workspaces/rpi/image
dd if=2024-11-19-raspios-bookworm-arm64.img of=/dev/sdX bs=4M
dd if=2026-09-15-raspios-trixie-arm64.img of=/dev/sdX bs=4M
</syntaxhighlight>
</syntaxhighlight>

Latest revision as of 15:02, 9 October 2026

Introduction

This page describes how to set up self-compiled Qt6 with WebEngine on Raspberry Pi 4 running Raspberry Pi OS.

To see other guides visit:

Setup

To be able to cross compile Qt6 for Raspberry Pi OS, we need three things:

Qt6 uses cmake therefore to cross-compile we need to have actually two builds of Qt. One Qt6 host build, which is later used to cross-compile the arm64 Qt build.

In this guide we are going to cross compile Qt 6.12 with QtWebEngine 6.140.

  Note: As shown here, we could do a 'top level build' of Qt6, but we are going to do 'prefix module build' just to show an alternative way of building Qt6.

As latest Raspberry Pi runs on wayland we need five repositories to have Qt 6WebEngine up and running: qtbase, shadertools, qtdeclarative, qtwebengine and qtwayland.

  • Let's create our workspace:
    mkdir -p ~/workspaces/rpi/toolchain/sysroot
    mkdir -p ~/workspaces/rpi/toolchain/qt/build/host
    mkdir -p ~/workspaces/rpi/toolchain/qt/build/target
    mkdir -p ~/workspaces/rpi/target
    mkdir -p ~/workspaces/rpi/image
    
  • Let's checkout the main qt6 repository, select 6.12 branch and fetch source code of minimal subset of qt modules to be able to compile QtWebEngine:
    cd ~/workspaces/rpi/toolchain/qt
    git clone git://code.qt.io/qt/qt5.git qt6
    cd qt6
    git checkout origin/6.12
    ./init-repository --module-subset=qtbase,qtshadertools,qtdeclarative --no-resolve-deps
    

  Note: Re-selecting other branches requires rerun of init-repository script with option '-f'.

  • Let's now clone qtwebenigne repository and switch to 6.140 branch :
    cd ~/workspaces/rpi/toolchain/qt
    git clone git://code.qt.io/qt/qtwebengine.git qtwebengine
    git checkout origin/6.140
    git submodule update --init
    

Sysroot

To be able to cross-compile, we need a part of arm64 target sysroot to be present on our build machine. As shown here, you could simply start your Rasberry Pi OS on the board and then rsync the required libs and includes to the host machine with SSH connection. However, in this guide we will mount the Raspberry Pi OS image via a loop device, use qemu to install build dependencies and than make a local copy.

  Note: We do need a local copy of 'libs' and 'headers' from the image, as we want to adjust the symlinks.

  • Let's now download Raspberry Pi OS:
    cd ~/workspaces/rpi/image
    wget https://downloads.raspberrypi.com/raspios_arm64/images/raspios_arm64-2026-09-15/2026-09-15-raspios-trixie-arm64.img.xz
    
  • Let's extract the image:
    unxz --keep 2026-09-15-raspios-trixie-arm64.img.xz
    
  • We need add at least 16G space to the image to be able to fit qt build with debug info (see here instructions)
  • Make loop devices for the image:
    losetup -fP 2026-09-15-raspios-trixie-arm64.img
    losetup -a
    /dev/loop0: [64769]:10797192 (~/workspaces/rpi/image/2026-09-15-raspios-trixie-arm64.img)
    
  • Mount the root partition:
    mount /dev/loop0p2 -t ext4 ~/workspaces/rpi/target
    mount -t proc /proc ~/workspaces/rpi/target/proc
    mount -t sysfs /sys ~/workspaces/rpi/target/sys
    mount --bind /dev ~/workspaces/rpi/target/dev
    mount --bind /dev/pts ~/workspaces/rpi/target/dev/pts
    
  • Make sure you have working setup of qemu arm64 static interpreter.

  Note: Setup of qemu static interpreter is distribution specific. Please refer to your Linux distribution documentation.

  Note: Also registering bin formats is distribution specific. Check 'systemctl status binfmt' if you distribution uses systemd.

  • We need qemu on the image to able to execute command, therefore copy static qemu binary to image.
    cp /usr/bin/qemu-aarch64-static ~/workspaces/rpi/target/usr/bin/
    
  • Chroot to arm64 target:
    cd ~/workspaces/rpi/target
    chroot .
    
  • Go to the apt sources file, look for the line that says 'Types: deb' and change it to include 'deb-src':
    vi /etc/apt/sources.list.d/debian.sources
    
    Types: deb deb-src
    URIs: http://deb.debian.org/debian/
    Suites: trixie trixie-updates
    Components: main contrib non-free non-free-firmware
    ...
    
  • Let's upgrade the image (optional) and install Qt6 build dependencies:
    apt update
    apt upgrade
    apt build-dep libqt6webenginecore6
    
  • exit and rsync the parts that we need:
    exit
    cd ~/workspaces/rpi/toolchain
    rsync -av ~/workspaces/rpi/target/lib sysroot/lib
    rsync -av ~/workspaces/rpi/target/usr/include sysroot/usr
    rsync -av ~/workspaces/rpi/target/usr/lib sysroot/usr
    rsync -av ~/workspaces/rpi/target/usr/share/pkgconfig sysroot/usr/share
    
  • Adjust symlinks to be relative:
    wget https://raw.githubusercontent.com/Kukkimonsuta/rpi-buildqt/master/scripts/utils/sysroot-relativelinks.py
    chmod +x sysroot-relativelinks.py
    ./sysroot-relativelinks.py sysroot
    

  Note: Command-line tool called symlinks should not be used instead as it creates dangling links.

  Note: We umount the image and detach loop device in Cleanup section

Toolchain

To cross-compile Qt, we need a cross-compiler toolchain. There are many excellent tools to compile a toolchain, such as crosstool-ng. We could also simply download ready 3dparty toolchain form the internet. However, this guide will use the official gcc cross compiler, which is shipped with Debian distribution, same as the one shipped with Raspberry Pi OS. In time of writing this quide latest Raspberry Pi OS is called 'Trixie' and it is shipped with gcc 14.2 and Qt 6.8. Therefore we will use the gnu gcc cross compiler from Trixie release. To setup the cross-compiler toolchain will use 'debootstrap' which is a very convenient tool to install Debian base system into a subdirectory of any other running Linux system. Most of distributions ship this tool therefore it was selected as most generic approach.

  Note: The other approach could be for example installing Debian distribution on VM.

  • To setup Trixie Debian release on your machine execute:
    debootstrap --arch amd64 trixie ~/workspaces/rpi/toolchain http://ftp.uk.debian.org/debian
    
  • Let's mount and chroot into newly created installation:
    mount -t proc /proc ~/workspaces/rpi/toolchain/proc
    mount -t sysfs /sys ~/workspaces/rpi/toolchain/sys
    mount --bind /dev ~/workspaces/rpi/toolchain/dev
    mount --bind /dev/pts ~/workspaces/rpi/toolchain/dev/pts
    
    chroot ~/workspaces/rpi/toolchain /bin/bash
    
  • Let's install host, cross compiler and mold linker:
    apt update
    apt install build-essential crossbuild-essential-arm64 mold
    

Qt6 Host Build

We need Qt6 host build as cross-compilation requires all host compiled tools like for example 'moc'. We will do here qt 'module' build in separate build directory.

  Note: For more information about build options see here.

  Note: After toolchain setup, we should be still chrooted into our host Debian root (~/workspaces/rpi/toolchain).

  • Add source packages repository for 'apt' by following line to '/etc/apt/sources.list' file:
    deb-src http://deb.debian.org/debian trixie main
    
  • Let's install dependencies for Qt host build using know dependencies for Qt 6.8:
    apt update
    apt build-dep libqt6webenginecore6
    
  • Let's configure host build for Qt6, build it and install into ~/workspaces/rpi/toolchain/opt/qt/host:
    cd /qt/build/host
    mkdir qtbase
    cd qtbase 
    /qt/qt6/configure -release -nomake tests -nomake examples -linker mold -prefix /opt/qt/host
    cmake --build . --parallel
    cmake --install .
    

  Note: The path /qt/qt6/qtbase in chroot is really ~/workspaces/rpi/toolchain/qt/qt6/qtbase on the build machine.

  • To be able to cross compile QtWebEngine module we also need gn, therfreo let's configure and build one from qtwebengine:
    cd /qt/build/host
    mkdir qtwebengine
    cd qtwebengine
    /opt/qt/host/bin/qt-configure-module ../../../qt6/qtwebengine -- -DBUILD_ONLY_GN=ON
    cmake --build . --parallel
    cmake --install .
    cd ..
    

  Note: We could build the whole module, but we just need gn binary, therfore we use BUILD_ONLY_GN define.

Qt6 Target Cross Build

We should have by now Qt6 host build, target arm64 sysroot and cross compilation toolchain. To setup now cmake build we still need to create cmake toolchain file 'rpi.toolchain.cmake', which will provide information to cmake about our toolchian and sysroot setup.

  Note: After toolchain setup, we should be still chrooted into our host Debian root (~/workspaces/rpi/toolchain).

  • Let's create toolchain file:
    vi /qt/rpi.toolchain.cmake
    
    cmake_minimum_required(VERSION 3.18)
    include_guard(GLOBAL)
    
    set(CMAKE_SYSTEM_NAME Linux)
    set(CMAKE_SYSTEM_PROCESSOR arm)
    
    set(CMAKE_SYSROOT /sysroot)
    set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc-14)
    set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++-14)
    
    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)
    
    set(QT_COMPILER_FLAGS "-march=armv8-a")
    set(QT_COMPILER_FLAGS_RELEASE "-O2 -pipe")
    set(QT_LINKER_FLAGS "-Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed")
    
    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()
    
    # See output of 'pkg-config --variable pc_path pkg-config' command.
    set(ENV{PKG_CONFIG_PATH} "")
    set(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_SYSROOT}/usr/lib/aarch64-linux-gnu/pkgconfig:${CMAKE_SYSROOT}/usr/lib/pkgconfig:${CMAKE_SYSROOT}/usr/share/pkgconfig)
    set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT})
    

  Note: If you wonder why we overload 'cmake_initialize_per_config_variable' see this blog post.

  Note: The pkg-config exports could be set in build shell instead of toolchain.

  • Let's configure target build for Qt6 qtbase, build it and install into ~/workspaces/rpi/toolchain/opt/qt/target:
    cd /qt/build/target
    mkdir qtbase
    cd qtbase 
    /qt/qt6/configure -release -opengl es2 -nomake examples -nomake tests -qt-host-path /opt/qt/host -extprefix /opt/qt/target -prefix /usr/local/qt6 -- -DCMAKE_TOOLCHAIN_FILE=/qt/rpi.toolchain.cmake
    cmake --build . --parallel
    cmake --install .
    
  • Let's configure and build QtWebEngine module:
    cd /qt/build/target
    mkdir qtwebengine
    
    cd qtwebengine
    /opt/qt/target/bin/qt-configure-module ../../../qt6/qtwebengine
    cmake --build . --parallel
    cmake --install .
    cd ..
    

Simplebrowser

To be able to test QtWebengine we can build 'simplebrowser' example.

  • first to able to run the example on the device we need a proper RPATH set for our target application, therefore edit example's 'CMakeLists.txt' and add the correct setting for our setup:
    vi /qt/qt6/qtwebengine/examples/webenginewidgets/simplebrowser/CMakeLists.txt
    set(CMAKE_INSTALL_RPATH "\$ORIGIN/../../../lib")
    

  Note: You do not have to st RPATH, instead use can export LD_LIBRARY_PATH to point to Qt installed libs on the device.

  • now let's configure and build the example:
cd /qt/build/target
mkdir simplebrowser
cd simplebrowser

/opt/qt/target/bin/qt-cmake ../../../qt6/qtwebengine/examples/webenginewidgets/simplebrowser -DCMAKE_INSTALL_PREFIX=/opt/qt/target/ -G Ninja
cmake --build .
cmake --install .

Deploy

We need to deploy compiled sources to sdcard. We can use RasberryPi OS image from sysroot step, mount it and rsync the content.

  Note: We are not any longer in the chrooted enviroment, however target image is still mounted.

  • Deploy with rsync:
    rsync -av ~/workspaces/rpi/toolchain/opt/qt/target/ ~/workspaces/rpi/target/usr/local/qt6/
    

  Note: If you wnat to rsync sysroot over the network, just replace the rsync steps below with the equivalent network location of your Rasberry Pi board.

Cleanup

During our toolchain preparastion we have left few mounts behind, therfore it is time to umount evetyhing

  • Umount no longer needed mounts:
    umount ~/workspaces/rpi/target/sys ~/workspaces/rpi/target/proc 
    umount ~/workspaces/rpi/target/dev/pts ~/workspaces/rpi/target/dev
    umount ~/workspaces/rpi/target
    
  • Detach loop device:
    losetup -d /dev/loop0
    

Write image

It is time to copy the image to the SD card (replace sdX with the device on your machine):

cd ~/workspaces/rpi/image
dd if=2026-09-15-raspios-trixie-arm64.img of=/dev/sdX bs=4M