Du liest meine Anleitung, wie ich Qt 5.15 samt QtWebEngine für einen Raspberry Pi 3 cross-kompiliert habe, so, dass die Standard-Grafikbibliotheken von Broadcom genutzt werden, die bei RaspiOS dabei sind. Die ganze Situation mit den Grafikbibliotheken ist ziemlich kompliziert, und ich versuche, sie im Anschluss zu erklären, aber hier erstmal die Anleitung:


Qt Build vorbereiten

Auf dem Raspberry Pi

  1. Den Pi updaten. rpi-update ist nicht nötig!
  2. Abhängigkeiten intallieren
  3. Ein Verzeichnis für Qt anlegen
sudo apt update
sudo apt full-upgrade

sudo apt install libfontconfig1-dev libdbus-1-dev libnss3-dev libxkbcommon-dev libjpeg-dev libasound2-dev libudev-dev libgles2-mesa-dev

sudo mkdir /usr/local/qt5pi
sudo chown -R pi:pi /usr/local/qt5pi

Die Abhängigkeiten sind:

fontconfigstellt Schriftarten für Qt bereit
dbusInter-Prozess-Kommunikation
nssSSL und so
xkbcommonTastatur-Handling
jpegjpeg halt…
asoundALSA. Wird nur benötigt, wenn Sound erwünscht ist.
udevUnterstützung für Maus-/Tastatur-Hotplugging
gles2-mesaDie Mesa-Version der Grafiktreiber, mitsamt Headern. Mehr dazu später.

Auf dem Host-Rechner

  1. Abhängigkeiten installieren
sudo apt-get install g++-multilib python pkg-config gperf bison flex libnss3-dev
  1. Cross-Kompilierungs-Toolchain installieren
sudo mkdir /opt/raspiToolchains
sudo chown 1000:1000 /opt/raspiToolchains
cd /opt/raspiToolchains
wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
tar xf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
rm gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz

https://github.com/raspberrypi/tools ist total veraltet und verwendet noch gcc 4.8, also verwende lieber eine von Linaro. Weil C++ recht strikt ist, was Abwärtskompatibilität angeht, kannst du einfach die neuste Version nehmen und alles sollte funktionieren.

  1. Ein Build-Verzeichnis anlegen
sudo mkdir /opt/qt5pi
sudo chown 1000:1000 /opt/qt5pi
cd /opt/qt5pi
  1. Ein Tool für Symlinks runterladen
wget https://raw.githubusercontent.com/riscv/riscv-poky/master/scripts/sysroot-relativelinks.py
chmod +x sysroot-relativelinks.py

Das ist einfach ein nützliches kleines Skript von jemandem auf Github, das wir später benutzen werden, um absolute Verknüpfungen zu relativen zu machen.

  1. Den Qt-Quellcode runterladen
git clone https://github.com/qt/qt5.git
cd qt5
git checkout 5.15
./init-repository --module-subset=essential,qtwebengine

Ich nehme lieber das git-Repo als den qt-everywhere-Download, weil es modularer ist und auch einfacher aktuell zu halten. Wenn ich einen branch auschecke, nehme ich lieber 5.15 als z.B. 5.15.0, weil 5.15 schneller aktualisiert wird und z.B. Sicherheitspatches für Chromium früher bekommt.

init-repository ist ein kleines Tool von Qt, um den Quellcode der angegebenen Module zu fetchen. Das “super-repo” selbst ist winzig, und alles weitere wird in git submodules verwaltet. Ich gebe hier an, dass ich alle essentiellen Module und QtWebEngine runterladen möchte. Welche Module es gibt steht hier, und nähere Infos zu dem Tool gibt’s mit init-repository --help oder hier.


Qt Build (bzw. Update)

Diese Schritte als nächstes ausführen, oder auch jedes mal, wenn du Qt aktualisieren möchtest.

Auf dem Host-Rechner

  1. Den (aktualisierten) sysroot vom Pi holen und Verknüpfungen relativ machen
cd /opt/qt5pi/
rsync -avzR pi@192.168.1.101:/lib :/usr/include :/usr/lib :/usr/share/pkgconfig/ :/opt/vc  sysroot
./sysroot-relativelinks.py sysroot

Das kopiert die angegebenen Verzeichnisse von deinem Pi über’s Netzwerk in ein Verzeichnis “sysroot”. Darin enthalten sind Bibliotheken, Header und pkg-config-Dateien, die wir später brauchen werden.

  1. Qt-Quellcode aktualisieren (falls nicht gerade erst runtergeladen)
cd /opt/qt5pi/qt5

(a) Ich möchte im gleichen branch (5.15) bleiben und neuere Änderungen pullen:
	git pull
	git submodule update --recursive
(b) Ich möchte zu einem anderen branch wechseln:
	git fetch
	git checkout 5.15
	git submodule update --recursive
(c) Normalerweise ist explizit ein commit von QtWebEngine im super-Repository ausgewählt. 
Wenn ich einen neueren haben möchte, muss ich einen branch von qtwebengine auschecken:
	git fetch
	cd qtwebengine
	git checkout 5.15
	git submodule update
(d) Wenn ich bereits einen branch ausgecheckt habe, für den es neuere commits gibt:
	cd qtwebengine
	git pull
	git submodule update
  1. Qt Essentials konfigurieren
cd /opt/qt5pi/qt5/qtbase/mkspecs/devices
cp -r linux-rasp-pi3-g++/ linux-rasp-pi3-brcm-g++/
nano linux-rasp-pi3-brcm-g++/qmake.conf

qmake.conf muss dazu angepasst werden. Diese Datei ist eine veränderte Version derer, die Qt mitbringt, und enthält einige Einstellungen zum kompilieren. So sollte sie aussehen:

# qmake configuration for the Raspberry Pi 3 using the Broadcom graphics stack
# MODIFIED to use the library names that are shipped since 2016
#
# Also setting the linker flags according to the pkg-config files that are shipped with the libraries.
# Remove egl.pc and glesv2.pc from /usr/lib/arm-linux-gnueabihf/pkgconfig/ for these to take effect.
# The reason for using static values instead of supplying the correct pkg-config files is that configure ignores -pthread from there
#
# Including headers from /opt/vc first, but also from mesa version of EGL and GLES, since the /opt/vc headers are from 2009 and don't work with qtwebengine.
# This way you can make mesa headers available by manually removing directories EGL, GLES and GLES2 from /opt/vc/include before building.

include(../common/linux_device_pre.conf)

# Let the linker know about /opt/vc/lib. Required for EGL config.test (at least) because brcmGLESv2 also needs brcmEGL.
QMAKE_RPATHLINKDIR_POST += $$[QT_SYSROOT]/opt/vc/lib

VC_LIBRARY_PATH         = $$[QT_SYSROOT]/opt/vc/lib
VC_INCLUDE_PATH         = $$[QT_SYSROOT]/opt/vc/include
MESA_INCLUDE_PATH	= $$[QT_SYSROOT]/usr/include

QMAKE_LIBDIR_OPENGL_ES2 = $${VC_LIBRARY_PATH}
QMAKE_LIBDIR_EGL        = $$QMAKE_LIBDIR_OPENGL_ES2
QMAKE_LIBDIR_OPENVG     = $$QMAKE_LIBDIR_OPENGL_ES2
QMAKE_LIBDIR_BCM_HOST   = $$VC_LIBRARY_PATH

QMAKE_INCDIR_EGL        = \
                        $${VC_INCLUDE_PATH} \
                        $${MESA_INCLUDE_PATH} \
                        $${VC_INCLUDE_PATH}/interface/vcos/pthreads \
                        $${VC_INCLUDE_PATH}/interface/vmcs_host/linux
QMAKE_INCDIR_OPENGL_ES2 = $${QMAKE_INCDIR_EGL}
QMAKE_INCDIR_BCM_HOST   = $$VC_INCLUDE_PATH

# recreating pkg-config --libs glesv2
QMAKE_LIBS_OPENGL_ES2   = -L$${VC_LIBRARY_PATH} -lbrcmGLESv2 -lbcm_host -lvcos -lvchiq_arm -pthread
# recreating pkg-config --libs egl
QMAKE_LIBS_EGL          = -L$${VC_LIBRARY_PATH} -lbrcmEGL -lbrcmGLESv2 -lbcm_host -lvchostif -lbcm_host -lvcos -lvchiq_arm -pthread
#recreating pkg-config --libs bcm_host
QMAKE_LIBS_BCM_HOST     = -L$${VC_LIBRARY_PATH} -lbcm_host -lvcos -lvchiq_arm -pthread

QMAKE_CFLAGS            = -march=armv8-a -mtune=cortex-a53 -mfpu=crypto-neon-fp-armv8
QMAKE_CXXFLAGS          = $$QMAKE_CFLAGS

DISTRO_OPTS            += hard-float
DISTRO_OPTS            += deb-multi-arch

EGLFS_DEVICE_INTEGRATION= eglfs_brcm

include(../common/linux_arm_device_post.conf)

load(qt_config)

Qt benutzt pkg-config, um herauszufinden, ob eine bestimmte Bibliothek auf dem Pi installiert ist und wenn ja, wo. Genau genommen gibt es oft mehrere Methoden dafür, und die letzte ist in der Regel die Verwendung der statischen Definitionen aus der Datei qmake.conf, die wir gerade gesetzt haben. Das heißt, dass wir alle .pc-Dateien entfernen müssen, damit configure nur unsere Definitionen bleiben und damit es nicht die Standard-Definitionen via pkg-config benutzt, die auf Mesa zeigen.

cd /opt/qt5pi/sysroot/usr/lib/arm-linux-gnueabihf/pkgconfig/
mv egl.pc egl.pc.mesa
mv glesv2.pc glesv2.pc.mesa

cd /opt/qt5pi
mkdir build
cd build

Jetzt können wir Qt für den Build konfigurieren, bzw. erstmal alles bis auf QtWebEngine (Erläuterungen rechts):

../qt5/configure \
-opengl es2 -device linux-rasp-pi3-brcm-g++ \
-device-option CROSS_COMPILE=/opt/raspiToolchains/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- \
-sysroot /opt/qt5pi/sysroot \
-prefix /usr/local/qt5pi \
-extprefix /opt/qt5pi/targetBinaries \
-hostprefix /opt/qt5pi/hostBinaries \
-opensource -confirm-license \
-release -v \
-nomake examples \
-no-use-gold-linker \
-recheck-all \
-skip qtwebengine \
2>&1 | tee ../configure$(date +"%Y-%m-%d_%H-%M").log
>Führe das configure-Tool aus
>mit unserer qmake.conf

>mit diesem compiler/toolchain
(Pfade ggf. anpassen)

>hier liegt der sysroot
>das wird später der Pfad auf dem Pi sein
>für den Pi bestimmte binaries hierhin legen
>für den Host bestimmte binaries hierhin legen
>Opensource-Variante von Qt verwenden
>release build, verbose logs
>nur libraries builden
>gold linker funktioniert nicht
>bei erneuter Ausführung alle Tests neu prüfen
>QtWebEngine überspringen – vorerst
>alles ins Protokoll schreiben

Überprüfe die Ausgabe; meine sah so aus.

  1. Build von Qt Essentials

Jetzt können wir den Build starten. Der make-Schritt dauert ca. eine halbe Stunde.

make -j4 2>&1 | tee ../make$(date +"%Y-%m-%d_%H-%M").log
make install 2>&1 | tee ../install$(date +"%Y-%m-%d_%H-%M").log
  1. QtWebEngine konfigurieren

Die Broadcom-Bibliotheken, die beim Pi dabei sind, sind zu alt, um QtWebEngine fehlerfrei zu builden – ihnen fehlt z.B. die GLES3-Unterstützung. Deshalb müssen wir sicherstellen, dass stattdessen die Mesa-Header gefunden werden. (Eigentlich ganz schön schräg, mehr dazu später.)
Außerdem muss der Build in einem separaten Verzeichnis durchgeführt werden.

cd /opt/qt5pi/sysroot/opt/vc/include
mv EGL EGL_brcm
mv GLES GLES_brcm
mv GLES2 GLES2_brcm

mkdir /opt/qt5pi/buildWebengine
cd /opt/qt5pi/buildWebengine
../hostBinaries/bin/qmake ../qt5/qtwebengine/ -- \
-webengine-pepper-plugins -webengine-proprietary-codecs -v \
2>&1 | tee ../configWebengine$(date +"%Y-%m-%d_%H-%M").log

Pepper Plugins wird benötigt, um später Widevine zu laden, und die Codecs sind für die Medienwiedergabe. Du kannst beides weglassen.
Überprüfe wieder die Ausgabe; meine sieht so aus.

  1. Build von QtWebEngine

Jetzt können wir den Build von QtWebEngine starten. Der make-Schritt dauert ca. 4 Stunden.

make -j4 2>&1 | tee ../makeWebengine$(date +"%Y-%m-%d_%H-%M").log
make install 2>&1 | tee ../installWebengine$(date +"%Y-%m-%d_%H-%M").log
  1. Upload auf den Pi
rsync -avz /opt/qt5pi/targetBinaries/ pi@192.168.1.101:/usr/local/qt5pi/

Die nachgestellten Schrägstriche sind wichtig!


Wenn du bis hierher gekommen bist, herzlichen Glückwunsch! Du solltest jetzt eine funktionierende Qt-Bibliothek auf deinem Raspberry Pi installiert haben. Ich erläutere noch kurz, wie man den Qt Creator einrichtet, um Programme ganz einfach cross-zu-kompilieren und über das Netzwerk auf den Pi zu bringen.

Qt Creator einrichten

Öffne den Projekte-Tab und gehe auf Kits verwalten….
Gehe auf den Qt Versionen-Reiter und füge eine neue Version hinzu, indem du das qmake auswählst, das du gerade kompiliert hast (/opt/qt5pi/hostBinaries/bin/qmake).

Füge unter dem Compiler-Reiter ein neues “GCC” für C und C++ hinzu.
Für C++ ist der Compiler-Pfad /opt/raspiToolchains/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++, und ich nenne ihn G++ RasPi Cross 7.5.0 (Linaro).
Für C ist der Compiler-Pfad /opt/raspiToolchains/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc, und ich nenne ihn GCC RasPi Cross 7.5.0 (Linaro).

Gehe auf den Geräte-Tab und lege deinen Pi als neues “Generic Linux Device” an. Du wirst nach der IP des PIs und nach einem Benutzernamen gefragt.

Gehe zurück zum Kits-Tab und erstelle im Kits-Reiter ein neues Kit. Ich nenne meins RasPi Cross 5.15. Wähle die Compiler (C und C++) und die Qt Version aus, die du gerade erstellt hast. Der Device Type ist Generic Linux Device, und dann kannst du unter Device das Gerät auswählen, das du gerade angelegt hast.

Jetzt kannst du das neue Kit aktivieren. Als letztes musst du noch dafür sorgen, dass in der .pro-Datei deines Projektes die folgenden Zeilen stehen:

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /home/pi/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

Jetzt sollte der Build funktionieren. Wenn du auf Ausführen klickst, wird Qt Creator das fertig kompilierte Projekt auf den Pi hochladen, ausführen und die Konsolenausgabe zu dir zurück leiten. Genial!


Die Situation mit den Grafik-Bibliotheken

Beim Raspberry Pi 3 sind normalerweise Grafiktreiber vom Chip-Hersteller Broadcom dabei und standardmäßig aktiviert. Für unsere Belange bestehen diese aus Bibliotheken für EGL (Embedded Graphics Library) und GLES (OpenGL for Embedded Systems) und liegen unter /opt/vc/lib als libbrcmEGL.so und libbrcmGLESv2.so.

Alternativ gibt es eine Open-Source-Version von Mesa. Das ist, was wir vorhin via libgles2-mesa-dev installiert haben. Diese liegen unter /usr/lib/arm-linux-gnueabihf/ als libEGL.so und libGLESv2.so.

Offensichtlich sind die beiden Versionen unterschiedlich benannt. Das geht zurück auf eine Entscheidung der RasPi-Foundation im Jahr 2016. Ist nicht so super, aber so ist’s nunmal. Es sei denn, man führt irgendwann rpi-update aus. Das Firmware-Update bringt die Bibliotheken tatsächlich mit den Standard-Namen im gleichen Ordner zurück, beide Bibliotheken erscheinen ansonsten identisch zu sein. Ich habe keine Ahnung, warum diese Entscheidung getroffen wurde, aber wenn man so will, heißt das, dass wir mit drei verschiedenen Versionen der Bibliothek umgehen müssen.

Zu den Bibliotheken gehören auch die Header-Dateien, die benötigt werden, um Software zu builden, die diese benutzt. Praktischerweise enthalten die ein Revisionsdatum:

Broadcom (/opt/vc/include/EGL/eglext.h)

/* Header file version number */
/* Current version at http://www.khronos.org/registry/egl/ */
/* $Revision: 7244 $ on $Date: 2009-01-20 17:06:59 -0800 (Tue, 20 Jan 2009) $ */

Mesa (/usr/include/EGL/eglext.h)

/*
** This header is generated from the Khronos EGL XML API Registry.
** The current version of the Registry, generator scripts
** used to make the header, and the header can be found at
**   http://www.khronos.org/registry/egl
**
** Khronos $Git commit SHA1: cb927ca98d $ on $Git commit date: 2019-08-08 01:05:38 -0700 $
*/

Die VC-Header sind von 2009 und ihnen fehlt, um nur ein Beispiel zu nennen, EGLSetBlobFuncANDROID. Außerdem fehlt GLESv2 der komplette Ordner GLES3/. Das heißt im Grunde, diese Header-Sammlung ist nutzlos, um etwas wie Chromium zu builden, was unausweichlich irgendwelche der neueren Definitionen benutzen wird.

Glücklicherweise habe ich herausgefunden, dass man einfach die neueren Header von Mesa benutzen kann, und trotzdem die Broadcom-Treiber zum “linken” verwenden kann. Das ist, was in Schritt 13 passiert. Wie zukunftsträchtig das ist, weiß man natürlich nicht, aber aktuell funktioniert es! Würde Chromium irgendeine der neuen Funktionen benutzen wollen, ginge das natürlich nicht. Ich konnte aber bisher keine Probleme damit feststellen. Chromium spamt ab und zu ein paar Fehlermeldungen, die evtl. damit zusammenhängen könnten, aber sie scheinen nichts kaputtzumachen.

Mit den Schritten, die ich beschrieben habe, kann man also QtBase und den Rest der Qt Essential-Module kompilieren und gegen die Standard-Bibliotheken von Broadcom linken, und im Anschluss QtWebEngine gegen die neuere Version dieser Bibliothek. Somit kann man die Standard-Grafiktreiber von RaspiOS aktiviert lassen und dennoch Qt mit QtWebEngine benutzen!

Vielleicht schaue ich mir in Zukunft mal an, komplett zu den Mesa-Treibern zu wechseln. 🙂 Bis dahin finden sich bestimmt auch etliche andere Tutorials zur Verwendung der Mesa-Treiber im Internet.

Referenzen

Ich würde gerne auf ein paar Webseiten, Blogs und Tutorials verweisen, die mir sehr geholfen haben, so weit zu kommen. Vielleicht findest du dort Hilfe, falls du nicht weiterkommst. Außerdem bin ich natürlich super froh darüber, dass es sie gibt, denn ansonsten hätte ich das alles vermutlich nicht hinbekommen. Sie verdienen also auf alle Fälle eine lobende Erwähnung!

https://www.raspberrypi.org/forums/viewtopic.php?t=204778
https://ottycodes.wordpress.com/2019/02/21/cross-compiling-qt5-12-for-raspberry-pi3/
https://wiki.qt.io/RaspberryPi2EGLFS
https://wiki.qt.io/RaspberryPiWithWebEngine
https://www.tal.org/tutorials/building-qt-512-raspberry-pi
https://mechatronicsblog.com/cross-compile-and-deploy-qt-5-12-for-raspberry-pi/
http://www.bildungsgueter.de/RaspberryIntro/Pages/GPULibs001.htm

https://github.com/UvinduW/Cross-Compiling-Qt-for-Raspberry-Pi-4 [Dank an Manish Buttan für diesen Link; RPi4 und ohne QTWebEngine, aber ansonsten gut strukturiert und aktuell]

2 Thoughts on “Qt 5.15 (samt QtWebEngine) Cross-Kompilieren für den Raspberry Pi 3

  1. Marco Langer on 9. Januar 2022 at 13:56 said:

    Leider komme ich bei Punkt 9 nicht weiter!
    Fehlermeldung:
    pi@raspberrypi:/opt/qt5pi $ rsync -avzR pi@192.168.178.101:/lib :/usr/include :/usr/lib :/usr/share/pkgconfig/ :/opt/vc sysroot
    ssh: connect to host 192.168.178.101 port 22: No route to host
    rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]
    rsync error: unexplained error (code 255) at io.c(228) [Receiver=3.2.3]

    Bis dahin konnte ich alles nach Anleitung installieren!?

    Grüße
    Marco

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Post Navigation