Building Qt and QtWayland for Raspberry Pi

Jon Trulson


Jon is a Senior Software Engineer that specializes in Linux/Unix and embedded systems development. Jon also contributes to technical presentations and the This Week in Qt blog for ICS.

By Jon Trulson | Wednesday, November 27, 2013

This document will explain how to build Qt 5.2.0 beta1 and QtWayland for Raspberry Pi (http://www.raspberrypi.org/). The procedure will likely work with the Qt 5.2.0 release candidate and final release, once they become available.

There are a variety of HOWTOs and other documents on the web on how to build one or the other – this document will attempt to walk you through each step. As Qt and QtWayland are constantly under development, this document may not be precisely up to date going into the future.

For a good overview of Wayland, see the Wayland wiki page here .

We will build Qt 5.2.0 beta1 from the published qt-everywhere-opensource-src-5.2.0-beta1.tar.gz download archive as well as QtWayland using a cross compile environment. This is much faster than trying to compile all of this natively on the Raspberry Pi itself.

For the uninitiated – a cross compiler is a compiler used for building native binaries for one platform, using another. In this document, we will be using an Intel Ubuntu machine to compile native ARM binaries for Raspberry Pi. If you are using a Microsoft or Apple operating system, it is easy to install an Ubuntu distribution onto a virtual machine using software like VirtualBox or VMware.

One of our engineers at ICS, David Johnson, has published a document on building Qt 5 for Raspberry Pi at http://www.ics.com/blog/building-qt-5-raspberry-pi . We will build it today in a slightly different way, namely:

  • We will build Qt 5.2.0 beta1. For this document, we will not build the Qt tools (designer, creator, etc.).

  • We will use an NFS (Network File System) mount of the Raspberry Pi root files system on the machine on which we will do the actual compiling for building, rather than SD card/loopback mounts. This is much faster and has the added benefit of updating the target's root file system immediately for testing.

  • We will “pre-create” the prefix installation directories on both the host and the target as the root user. For both of these directories, we will make their permissions 777. This is to simplify building by not requiring that you be setuid root in order to do make install. The ownership and permissions of the host and target prefix directories can be changed to more secure settings once compiling is complete.

Conventions

For this document, we will use the following conventions:

  • The machine where we will do all of our compiling will be referred to as the host.

  • The Raspberry Pi will be referred to as the target.

  • A '$' (dollar sign) at the beginning of a command line will refer to a user shell prompt.

  • A '#' (number sign) at the beginning of a command line will refer to a root (superuser) shell prompt

Build Outline

  • Set up the host and target.

  • Build/install Qt 5.2.0 beta1 (cross-compiled for the target).

  • Build Wayland (on the host, for the wayland-scanner program).

  • Build/install QtWayland (cross-compiled for the target).

  • Test against the Weston and QtWayland compositors.

Setup

First, we will set up our host and target systems. In this document, the host is a quad core Intel machine running a 64-bit flavor of Kubuntu (the derivative of Ubuntu Linux that uses the Qt-based KDE graphical environment). Of course, the target is the ARM-based board we all know and love as the Raspberry Pi. In this exercise, we are using the older Model B, with 256MB of RAM and an 8GB Class 6 SD card.

Initial Host Setup

At a minimum, you will need the git package(s) and certain essential build tools installed on your host.

$ sudo apt-get install build-essential git autoconf automake libtool

Next, we will need to clone our cross compiler toolchain, download Qt 5.2.0 beta1, pre-create our mount point for the target root filesystem (or rootfs), and our prefix installation directory. So, starting from your home directory, lets create a directory in which to do all of our work, which will be referenced as $BDIR going forward.

Then we will clone our cross compiler toolchain. We will use the Linaro toolchain already created for the Raspberry Pi.

$ export BDIR=~/src/rpi
$ mkdir -p $BDIR
$ cd $BDIR
$ git clone https://github.com/raspberrypi/tools.git

This toolchain contains 32-bit binaries, so if you are using a 64-bit version of Ubuntu, you should install the 32-bit library meta package so the binaries can be executed. If you are running a 32-bit version of Ubuntu, then this is not needed.

$ sudo apt-get install ia32-libs

Next, we need to clone the Qt cross compile toolkit. This toolkit contains some 'helper' scripts for cross compiling Qt. We only need one script, fixQualifiedLibraryPaths, that we will use to correct some library paths on the target.

$ git clone git://gitorious.org/cross-compile-tools/cross-compile-tools.git

Now, let's download the Qt 5.2.0 beta1 Open Source edition tar archive and unpack it.

$ wget http://download.qt-project.org/development_releases/qt/5.2/5.2.0-beta1/single/qt-everywhere-opensource-src-5.2.0-beta1.tar.gz
$ tar xvf qt-everywhere-opensource-src-5.2.0-beta1.tar.gz

Next, we need to check out QtWayland separately, since it has not been officially supported as part of any Qt release yet.

$ git clone git://gitorious.org/qt/qtwayland.git

Work is always ongoing in this module, so you will probably want to use the latest code whenever possible. Since we've just cloned the repository, there will not be any new code for you to retrieve, however, should you wish to update from time to time, the following can be done:

$ cd qtwayland
$ git pull
$ cd ..

The QtWayland module provides three example Wayland compositors as well as the wayland_brcm platform plugin needed to allow Qt applications to use Wayland for rendering.

Next, we need to get the Wayland repository. All we need from this repository is the wayland-scanner executable, which we will build later. This executable will be needed in order to build QtWayland.

$ git clone git://anongit.freedesktop.org/wayland/wayland

So now that all the code we need is there, lets create a couple of directories on the host. The first will be the prefix directory, under which the host specific Qt build products will be placed, such as qmake, moc, etc. When these tools are compiled, they will be compiled for the host, not the target, since they are needed only on the host for building Qt and Qt applications.

We will create the directory with read-write access for all (777).

This will allow us to update the contents of that directory (by doing a make install) without having to be the root user.

$ sudo mkdir /opt/qt5-rpi
$ sudo chmod 777 /opt/qt5-rpi

The directory /opt/qt5-rpi will be passed as the '-prefix' option to the configure command later on, when we start building Qt5. During a make install, host specific binaries and files will be installed here.

Next, create a mount point for the target's root filesystem (rootfs). This will be the location where the Raspberry Pi's root will be mounted over NFS.

$ sudo mkdir /mnt/rpi-rootfs
$ sudo chmod 755 /mnt/rpi-rootfs

Initial Target Setup

The first thing to do when setting up your Raspberry Pi (target) is to get an appropriate Linux boot image. For this document, I am using the Raspbian distribution, which you can find at http://www.raspberrypi.org/downloads. I used 2012-09-18-wheezy-raspbian.zip, and an 8 GB SD card. The Raspberry Pi used is an original Model B with 256MB of memory.

For instructions on getting the image onto the card, you can go to http://elinux.org/RPi_Easy_SD_Card_Setup. If you are familiar with the Linux command line, and the dd program, it is straightforward – simply unzip the file and use dd to copy it onto the SDcard. Be careful when doing this so you do not accidentally copy the image over your computer's hard disk! That will ruin your day. See the link above for more information as well as other methods that can be used.

When booting the target for the first time, you will be provided with various options:


If you used an SD card larger than 2GB, you should select 1. Expand Filesystem. This will allow the filesystem size to increase and use your entire memory card.

Do not enable option 3. Enable boot to Desktop. You will always want the target to boot in text mode during this exercise.

Select 8. Advanced Options, and from that menu, select A4. SSH to enable the ssh server. You will generally find it much easier to use the target via an ssh connection. When the target boots, it will show it's IP address. For the rest of this document, it will be assumed you are logged into the target via an ssh connection.

You should be able to log in to the target over ssh with a command like:

$ ssh pi@x.x.x.x

Where x.x.x.x is the target's IP address. The password is 'raspberry'.

Under Advanced Options, I would also recommend selecting A3. Memory Split and selecting 128MB for memory to reserve for the GPU.

For the other options present, select those that may apply to you. You can always run the following command to re-enter the setup utility after installation by executing the following command:

$ sudo raspi-config

Once installed, there are several packages needed for development, as well as for NFS support.

First, let's get NFS working. Login to the target using ssh, or if you prefer, the console. The default user is 'pi', with a password of 'raspberry'.

We need to install the nfs-kernel-server package first and configure it.

$ sudo apt-get install nfs-kernel-server
$ sudo vi /etc/exports

Add the following line to the /etc/exports file and save it:

/      *(rw,sync,no_subtree_check,no_root_squash,insecure)

This configures the NFS server to share the root filesystem to all machines in your network. Obviously, this is not secure so do not do this unless your machine is protected by a firewall.

NFS is almost ready, however there is a problem with the RPC portmap utility rpcbind. This is required for proper operation of the NFS server and is already installed by default onto the target. The problem is that it is not configured properly to start up when needed, so if you just reboot your target, the NFS server will fail to start because rpcbind isn't running.

To fix it, execute the following commands:

$ sudo -i
# cd /etc/rc2.d
# ln -s ../init.d/rpcbind S01rpcbind
# update-rc.d rpcbind defaults
# exit

Now when you reboot your target, NFS should start and the root filesystem will be available to your host.

Next, we need to install a large number of packages for development. Each 'sudo apt-get install' line is a separate line.

$ sudo apt-get install libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm4 libxcb-icccm4-dev libxcb-sync0 libxcb-sync0-dev libxcb-render-util0 libxcb-render-util0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-glx0-dev

$ sudo apt-get install libxkbcommon-dev libudev-dev libwayland-dev libpng12-dev libjpeg8-dev libmtdev-dev autotools-dev autoconf automake bison flex libasound2-dev libxext-dev x11proto-xext-dev libxcursor-dev libxfixes-dev x11proto-fixes-dev libffi-dev libsm-dev libxcomposite-dev

Now that we have our required packages installed, lets create the /opt/qt5-rpi installation directory with permissions that will allow us (on the host via NFS) to install the target Qt components without being a super user.

$ sudo mkdir /opt/qt5-rpi
$ sudo chmod 777 /opt/qt5-rpi

Next, we need to make some edits to the target's /boot/config.txt file. The file specifies certain boot time configuration options for the target. We will want to add a boot time configuration option that will allow Wayland to behave a little better with the Raspberry Pi's limited frame buffer memory and performance:

$ sudo vi /boot/config.txt

Add the following line:

dispmanx_offline=1

This option will allow an off-screen buffer to be allocated for use in compositing when there are many elements on the screen. This is highly recommended. Without this option, you will see random screen blanking while applications are running, depending on the application.

In addition, verify that gpu_mem is equal to 128. If you followed the instructions above with the raspi-config utility during installation, then gpu_mem should already be set to the right value.

Then save the file and reboot your target so the change takes effect. Now, we should have everything ready to start compiling everything.

Building

The first thing to do is to mount the target's root filesystem to the mount point on the host we created earlier. So, on the host, run:

$ sudo mount x.x.x.x:/ /mnt/rpi-rootfs

where x.x.x.x is the IP address of your Raspberry Pi. This address will be displayed on the console when the target boots.

Once this has been done, we need to run the fixQualifiedLibraryPaths command from the qt-xcomp-tools repo we cloned earlier. This command will adjust some of the library symlinks on the target to not use absolute paths, which would cause compile errors since we are cross compiling. So, on the host, after you have mounted the target's rootfs, run the following (all on one line):

$ sudo $BDIR/cross-compile-tools/fixQualifiedLibraryPaths /mnt/rpi-rootfs/  $BDIR/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-g++ 

The first argument to the script is the location of the target's mounted rootfs. The second option is the full path to your C++ cross compiler.

It should output some lines indicating what it is doing.

Building Qt 5.2.0 Beta 1

Now we should be ready to configure and build Qt 5.2.0 beta1:

$ cd $BDIR/qt-everywhere-opensource-src-5.2.0-beta1

$ ./configure -opengl es2 -qpa eglfs -device rasp-pi -device-option CROSS_COMPILE=$BDIR/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf- -sysroot /mnt/rpi-rootfs -opensource -confirm-license -optimized-qmake -reduce-relocations -reduce-exports -release -nomake tests -prefix /opt/qt5-rpi -no-pch -skip qttools

The ./configure command should all be on one line. Next, we compile and install it. My host is a quad-core machine so I'll pass the -j argument to make telling it how many commands to run in parallel.

In addition, we want our newly compiled qmake executable (when compiled) to be the first in the PATH, this will be important later when we want to build QtWayland.

$ export PATH=/opt/qt5-rpi/bin:$PATH
$ make -j4 install

After some time, and assuming no errors occurred, Qt 5.2.0 beta1 will be compiled and installed on both the host and target. On the host, in /opt/qt5-rpi/ you will find host specific binaries (in bin/), mkspecs (in mkspecs/), and host specific bootstrap libraries (in libs/).

On the target, you will find the cross-compiled Qt 5.2.0 beta1, with all libraries and headers installed. If you log into the target via ssh, you will find Qt in /opt/qt5-rpi/, or you can look from the host perspective by looking at the target's mounted rootfs in /mnt/rpi-rootfs/opt/qt5-rpi/.

Building Your Own Qt Applications

If you wish to build your own Qt applications, all the pieces are now present to do so. Simply ensure that /opt/qt5-rpi/bin is first in your path so that you get the correct qmake, then do the usual qmake, and make.

Once complete, you can then copy your compiled binary over to the to target (using NFS if you like) and run it.

Building Wayland

The only thing we need from the Wayland repository is the wayland-scanner executable. This program is used by QtWayland's build process to generate some code that will be compiled to build QtWayland itself.

Therefore, we will configure and compile it. Then we will copy the wayland-scanner executable into a directory in our PATH, like /opt/qt5-rpi/bin.

$ cd $BDIR/wayland
$ ./autogen.sh
$ ./configure --disable-documentation
$ make
$ cp src/wayland-scanner /opt/qt5-rpi/bin/

Building QtWayland

Now we want to build and install QtWayland. In order for this to work, you must have the 'correct' qmake and wayland-scanner executables in your PATH. If you have been following along, then this should already be the case, but just to make sure, execute the following commands and make sure the output matches:

$ which qmake
/opt/qt5-rpi/bin/qmake
$ which wayland-scanner
/opt/qt5-rpi/bin/wayland-scanner

It's important that these are pulled from the correct place or mayhem will follow. The qmake program in particular will cause a great deal of grief if you aren't using the right one.

Now we will build QtWayland. We will want to build both the wayland-brcm QPA platform plugin, as well as the example compositors.

$ cd $BDIR/qtwayland
$ qmake CONFIG+=wayland-compositor
$ make install

When you run qmake, you will get a diagnostic:

Project MESSAGE: no wayland-egl support detected, cross-toolkit compatibility disabled

This is normal. The EGL implementation on the Raspberry Pi does not support the Wayland specific extensions. The plugin for Raspberry Pi in QtWayland actually uses the Raspberry Pi specific EGL support that is present (brcm).

Assuming the build succeeded, we should be ready to try some example applications.

Testing With The Weston Compositor

At this point, everything should be compiled and ready to try out. As a first step, let's run a couple of simple Qt examples using the Weston compositor that is already installed on the Raspberry Pi. I would recommend doing this using an ssh session from the host to the target:

It's also best to run all of these as root, so after logging in, run:

$ sudo -i
# 

So, first we will start the Weston compositor in the background:

# weston-launch &

You should see the following on the Raspberry Pi's screen:


Now, in order to run applications on this compositor, we need to find out the value of the environment variable that weston-launch sets up, called $XDG_RUNTIME_DIR. This variable serves a similar function to the X11 $DISPLAY environment variable, providing a method whereby a Wayland client can talk to an appropriate Wayland compositor. To figure out what this variable is, you can click on that little terminal icon in the upper left corner. This will open up a terminal interface (weston-terminal). In that terminal, run the following command:

# env |grep XDG

In addition, you should see something like this:


Now that we know the value of that variable, we will go back to our ssh session, and export the same variable and value. We will also want to set the default platform plugin for Qt applications to the correct value so that they will use wayland-brcm platform plugin. In your ssh session:

# export XDG_RUNTIME_DIR=/run/shm/wayland-root-TSzkTV
# export QT_QPA_PLATFORM=wayland-brcm

Now, let's try a couple of the Qt examples:

# /opt/qt5-rpi/examples/widgets/effects/lighting/lighting &
# /opt/qt5-rpi/examples/widgets/graphicsview/collidingmice/collidingmice &

You should see something like the following:


Testing with Qt's Example Compositors

QtWayland includes 3 example compositors that you can try. They are qml-compositor, qwidget-compositor, and qwindow-compositor. You can test these compositors to see how they work if you like.

If you still have the Weston compositor running, you can terminate it with CTL-ALT-BACKSPACE before attempting to start any of the other compositors. Only one compositor should be running at any given time.

As an example, here's how you might start the qml-compositor and a client.

# export XDG_RUNTIME_DIR=/tmp
# export QT_QPA_PLATFORM=wayland-brcm
# cd /opt/qt5-rpi/examples/qtwayland/qml-compositor
# ./qml-compositor -platform eglfs &
# /opt/qt5-rpi/examples/widgets/graphicsview/collidingmice/collidingmice

Notice how we use the -platform eglfs option when starting the compositor. This will make the compositor itself use the eglfs platform plugin for rendering. The default would be to use the value of the QT_QPA_PLATFORM variable.

I would provide screen shots, however there is currently no mechanism available to do so.

Conclusion

I hope this article has allowed you to build a functional QtWayland installation. There is a great deal of work ongoing in both Qt and QtWayland, so frequent updating, building, and testing is worthwhile.

In time, QtWayland should make it into an official Qt release and become a fully supported and functional QPA backend for Qt.



Comments

Comment: 
Hi Jon, Thanks for the great tutorial. I am trying to follow your steps on Linux Minty 3.7.8, I could successfully complete until steps "Building Wayland". However I am getting following error when I am tring qmake in "Building QtWayland" step. "Project ERROR: QtWayland requires wayland-scanner" I double checked wayland-scanner and it's actually reachable. $ which wayland-scanner /opt/qt5-rpi/bin/wayland-scanner Do you know, what could be the issue? Thanks, Kunal

Comment: 

Hi Kunal,

That should be correct. One thing you should try is to actually execute /opt/qt5-rpi/bin/wayland-scanner and see if it runs. Without arguments, it should just print out usage information, like so:

$ /opt/qt5-rpi/bin/wayland-scanner
usage: ./scanner [client-header|server-header|code]

If wayland-scanner cannot execute, then you might see the error you reported...

Comment: 

Hi Jon,

It seems to be working. Here is the output.

kunal@Minty ~/src/rpi/qtwayland $ qmake CONFIG+=wayland-compositor
Project ERROR: QtWayland requires wayland-scanner

kunal@Minty ~/src/rpi/qtwayland $ wayland-scanner
usage: ./scanner [client-header|server-header|code]

Converts XML protocol descriptions supplied on stdin to client headers,
server headers, or protocol marshalling code.

Thanks,
Kunal

Comment: 

Sorry, I mean wayland-scanner is working but not the qmake. Can you please help?

Comment: 

Well at this point you should start looking at the test itself in qtwayland/config.tests/wayland_scanner to see what is going on. One of the things it does is to run wayland-scanner to see if it can produce output. On my current system, it tries to run:

wayland-scanner client-header < scanner-test.xml > wayland-scanner-test-client-protocol.h

Which works for me at least. Assuming yours is the same, what happens if you go into this directory and execute the above command?

Comment: 

Unfortunately, no output :(

kunal@Minty ~/src/rpi/qtwayland/config.tests/wayland_scanner $ wayland-scanner client-header < scanner-test.xml > wayland-scanner-test-client-protocol.h

It silently came out without any message.

Let me tell you one more thing, while I was compiling wayland, it was gave an error somthing like missig "expat". I couldn't find it using apt-get, so I downloaded the source and build it myself. After that I was able to compile. Can that be the cause? If so what could have I done?

Comment: 

That sounds normal... It doesn't output anything on my system either. However, it should have created a file, wayland-scanner-test-client-protocol.h, that should exist, and be non-zero in size.

I do not know if your building an expat repo would affect anything. I did not need to do this. You can clean out your qtwayland repo with:

git clean -dfx

Then retry qmake and see if anything useful happens.

Otherwise, you will just have to investigate yourself, by starting with the failing test in qtwayland/config.tests/wayland_scanner/ .

Comment: 

I use kubuntu 12.04 LTS to compile the qt5 for RPi, thanks for the doc, but when I compile qtwayland, there is errors:

$ qmake CONFIG+=wayland-compositor
Project WARNING: QtWayland requires Wayland 1.2.0 or higher
Project WARNING: QtWayland requires wayland-scanner
Project MESSAGE: no wayland-egl support detected, cross-toolkit compatibility disabled

$ which qmake
/opt/qt5-rpi/bin/qmake
$ which wayland-scanner
/opt/qt5/rpi/bin/wayland-scanner

$ wayland-scanner
usage: ./scanner [client-header|server-header|code]

Converts XML protocol descriptions supplied on stdin to client headers, server headers, or protocol marshaling code.

Comment: 

Awesome article. Thank you.

I've only used Linux and Qt as a developer since 2001 so I'm kind of new at it. Must learn more about how qmake handles tests and the like. Seriously any help would be appreciated here.

pierre@PierresLaptop:~/src/rpi/qtwayland$ qmake CONFIG+=wayland-compositor
Info: creating cache file /home/pierre/src/rpi/qtwayland/.qmake.cache
Checking for wayland... no
Checking for xkbcommon... yes
Checking for wayland_cursor... yes
Checking for wayland_scanner... no
Checking for wayland_egl... no
Checking for egl... no
Checking for brcm_egl... yes
Checking for glx... no
Checking for xcomposite... yes
Checking for drm_egl_server... no
Project WARNING: QtWayland requires Wayland 1.2.0 or higher
Project WARNING: QtWayland requires wayland-scanner
Project MESSAGE: no wayland-egl support detected, cross-toolkit compatibility disabled
pierre@PierresLaptop:~/src/rpi/qtwayland$ which qmake
/opt/qt5-rpi/bin/qmake
pierre@PierresLaptop:~/src/rpi/qtwayland$ which wayland-scanner
/opt/qt5-rpi/bin/wayland-scanner

Comment: 

Hi, the issue is that RPi wayland and qtwayland have all moved on to newer versions, while RPi raspbian is still using an older version or wayland that is not compatible with current qtwayland. I believe that there was a new version of raspbian for RPi in progress that was supposed to include the latest wayland. I believe it was called 'dora'. But we have not looked at this in a while.

Pages