Motiviation

The latest version of qt in the raspian package repo is 5.3.2. The latest release of qt is version 5.7. A lot of nice features and changes have come to qt since 5.3.2 was release on 2014-09-16.

The qt that ships with raspbian also expects to run inside of X11. While not inherently a bad thing, if you want to make a fullscreen kiosk program and have no need for a desktop environment, you can get back a non trivial amount of the available 512MB of ram, and also get a chunk of bootup time back.

If you do a targetted build of QT, you can run QML apps on EGL, giving qt a single opengl context and full control of the ui.

Compiling native code on the raspberry pi is slow. While the balance of setting up a full blown cross compiliation toolchain and just living with slow build times is usually a judgement call, having a working QT environment, and being able to use QT Creator or similar IDE on a dev pc makes it worth it.

Building Qt on a rpi2 directly may actually not be possible due to memory constraints. I don’t know, I haven’t tried

Overall Objective

Building a library for cross compilation involves the same basic tools:

A cross compiler: usualy gcc that run on the host pc that builds binaries for the target pc. In the raspberry pi’s case, you need a x86_64 friendly toolchain that makes elf executables for arm cpus. Usually the gcc cross compilers will have the longform or «triple» name, e.g. arm-none-eabi-gcc as opposed to just gcc.

A sysroot: For us, this is going to be a copy of the important libraries and headers from Raspbian’s os. If you have space, and don’t have the inclination to decide which files and folders are the important ones, you can just copy the entire filesystem heierachy over. This is especially understandable since they are kind of spread out and take a while to find.

A prefix: This is the path where your cross compiled programs are going to get installed to. It’s similar to the sysroot, in that it matches the file system layout of the target system. The difference is that this is where your compiled programs, libraries, and headers go.

Getting a Compiler

There are already compiled toolchains available on launchpad. Download the appropriate one for your platform and unpack it somehwere. Inside the bin folder in the unpacked folder you’ll see a few programs called arm-none-eabi-gcc, arm-none-eabi-g++ and arm-none-eabi-ld. These are the entrypoints to the cross compiler to work with. They run on your native platform, but build programs for your target platform

Building the Sysroot

In order to build for a target platform, you need headers and libraries for that platform. For most embedded systems, a vendor will provide these. For the pi, and other hobbiest embedded platforms, the easiest way to get these is to just copy them off the target device. rsync will do the job nicely.

rsync --verbose -P --recursive --times --ignore-errors --delete --copy-unsafe-links --links \
    --include '/' \
    --include '/etc' \
    --include '/lib' \
    --include '/lib/***.so*' \
    --include '/lib/arm-linux-gnueabihf/***' \
    --include '/opt' \
    --include '/opt/vc' \
    --include '/opt/vc/include/***' \
    --include '/opt/vc/lib/***' \
    --include='/usr/' \
    --include='/usr/include/***' \
    --include='/usr/lib' \
    --include='/usr/lib/***.a' \
    --include='/usr/lib/***.la' \
    --include='/usr/lib/***.o' \
    --include='/usr/lib/***.so*' \
    --include='/usr/lib/debug/***' \
    --include='/usr/lib/arm-linux-gnueabihf' \
    --include='/usr/lib/arm-linux-gnueabihf/***.a' \
    --include='/usr/lib/arm-linux-gnueabihf/***.la' \
    --include='/usr/lib/arm-linux-gnueabihf/***.o' \
    --include='/usr/lib/arm-linux-gnueabihf/***.so*' \
    --include='/usr/local' \
    --include='/usr/local/include/***' \
    --include='/usr/local/lib/***' \
    --include='/usr/src/***' \
    --exclude '*' \
    pi@raspberry.local:/ ~/raspbian-sysroot/

Blindly rsyncing everything also works pretty well. If you need to update the device (apt get update; apt get upgrade), or install additional libraries, dependencies just run the rsync command again to get all the new and updated files.

Normally, ld.so.conf includes the paths to .so files that applications can link to via a default search path. Natively, linux systems will parse everything in /etc/ld.so.conf.d/*.conf and treat it as a single collection library search paths. This doesn’t always work correctly for the cross compilers, so we can just concatinate them together into a single /etc/ld.so.conf

ssh pi@raspberry 'cat /etc/ld.so.conf.d/*.conf' > /srv/raspbian/etc/ld.so.conf

Building QT

Getting the Source Code

QT uses a model where they have a main git repo that mostly contains git submodules to other git components. Have a look at the tree view on the website to see what I am talking about. They then use branches to references different versions of various submodules.

git clone --recursive https://code.qt.io/qt/qt5.git
git checkout 5.7
git submodule update

Configure Qt

you are going to need to pick a build directory that is outside the source directory, outside the sysroot, and outside the prefix.

cd ~/qt5-rasbpian
~/qt5/configure -v -nomake examples -nomake tests -opengl es2 -device linux-rasp-pi2-g++ -device-option CROSS_COMPILE=~/gcc-arm-none-eabi-5_3-2016q1/bin/arm-none-eabi- -sysroot ~/rasbpian-sysroot/ -prefix /usr/local/qt5.7-release -extprefix ~/raspbian-extprefix/ -confirm-license -opensource -release

There is a lot going on in that one line, here is a breakdown of the paramters being passed to qt’s configure command:

  • -nomake examples
    • We don’t need cross compiled examples, and they take a long time to build.
  • -nomake tests
    • We don’t need the test suite, it takes a long time to build.
  • -opengl es2
    • This tells qt what opengl flavor to use. The rpi2 only supports opengl es2, so that’s the one we are using.
  • -device linux-rasp-pi2-g++
    • inside of qt5/qtbase/mkspecs/devices there is a folder called linux-rasp-pi2-g++. It’s worth looking in this folder at qmake.conf to see the changes that qt is going to apply when compiling itslef.
  • -device-option CROSS_COMPILE=~/gcc-arm-none-eabi-5_3-2016q1/bin/arm-none-eabi-
    • This option tells qt what to prefix each of its expected compile commands with. For native compiling, you would just use gcc and g++ now you need to tell it how to get the name for arm-none-eabi-gcc and arm-none-eabi-g++
  • -sysroot ~/raspbian-sysroot/
    • This is just the path to the sysroot you built earlier.
  • -prefix /usr/local/qt5.7-release
    • This is where the final path on the target is going to be. This is not where you want the host to put the files. Qt will use this when setting up absolute paths for linked binaries.
  • -extprefix ~/raspbian-extprefix/qt5.7-release
    • This is the host path where qt is going to compile programs to. This is not the path on the target.
  • -confirm-license -opensource
    • Only add these options if you agree to the Qt licensing terms
  • -release
    • tells Qt to build with optimized settings. Replace this option with -debug to get full debugging symbols and fewer optimizations.

Dealing With Issues

The Configure program may print out errors and warnings, and then exit before completing. If configure complains about a missing header, you need to:

  1. Find and install the package on debian that installs that header. The package name usually ends in -dev.
    • There is no hard and fast rule for matching a header up to the package that owns it. Google is your friend.
  2. Rerun the sysroot building rsync command. This will copy the newly installed libraries and headers over toy your host PC
  3. Run the configure command again.

Alternatively, you may be able to disable the feature in qt by passing an additional argument to «configure» e.g. -no-xcb

Compiling Qt

Once the configure command completes successfully, it’s time to compile qt.

make -j8

Adjust the number after j to match the number of cpu cores you have.

Deploying Qt

This is kind of the opposite of building a sysroot. You now need to sync your extprefix on the host to the root of the target device.

rsync -P --recursive --times --ignore-errors --delete --copy-unsafe-links --links ~/raspbian-extprefix/qt5.7-release pi@raspberry.local:/usr/local/