Cross Compiling Qt4 Applications for the Raspberry Pi

This blog post will demonstrate how to cross compile Qt4 applications for the Raspberry Pi (RPi).  The approach taken in this entry is a little different from that taken by other tutorials such as this or this. The complete cross compilation of the Qt4 or Qt5 libraries will NOT be covered. Instead,  the Qt4 libraries available in the Raspbian’s repos will be installed on the Raspberry Pi and the RPi’s root filesystem will then be mounted onto the PC over the network using SSHFS. The RPi’s  cross compiling toolchain will then build qt apps by compiling Qt4 source code on the PC and linking it to the Qt4 libraries available via the SSHFS-mounted RPi filesystem.

Simple QT4 application cross-compiled on PC

Simple QT4 application cross-compiled on PC

The challenge with such an approach is the inability to use either the native qmake on the PC or the RPi. Both were built to be used natively on their respective platforms. While cross-compiling qmake and customizing it may be possible, the approach presented here will circumvent qmake altogether. Instead, a custom makefile will be used to successfully cross-compile Qt4 applications.

My username on my Linux PC is ‘halherta’ and by RPI’s IP address is ‘192.168.0.104’. Please replace these with your appropriate username and RPI IP address as per your setup.

Bring up the RPi with Raspbian OS:

First we need to boot Raspbian on the Raspberry Pi. If you don’t already have Raspbian running on the RPi or need to do a clean install of Raspbian OS on the RPi, do the following:

  • Install the Raspbian OS on an SD Card (4GB should be OK but 8GB is better)
  • Plug the SD Card into the Raspberry Pi
  • Connect the Raspberry Pi to the Network and power it
  • SSH into the RPi.
  • Configure the RPi using the raspi-config utility
  • Reboot and SSH again onto the RPi
  • The first six steps listed above are covered in detail here.
  • Update all packages on the RPi with:
sudo apt-get update && sudo apt-get dist-upgrade
  • While still logged in to the RPi over SSH, install the Qt4 libraries on the Raspberry Pi from the Raspbian repos:
sudo apt-get install libqt4-dev

Install The RPi’s cross compiling Toolchain on your Linux PC

To install the RPi’s cross compiling toolchain on your Linux (Ubuntu-based/Debian-based distro) PC:

  • Create a $HOME/rpi directory on your Linux PC. Download the cross-compiling toolchain for the RPi into that directory on your Linux PC and add it’s bin directory  to the PATH variable in your .bashrc as instructed here .
  • Verify that the cross-compiling toolchain is installed on your PC by running the following command:
arm-linux-gnueabihf-gcc -v
  • If everything went OK you should get output that looks like this

Install SSHFS  and Qt4’s meta object compiler on your PC

  • After installing the RPi cross-compiling toolchain, we will need to install SSHFS on the PC. This will allow us to mount our RPi’s root filesystem onto our PC over the network.
sudo apt-get install sshfs
  • once SSHFS is installed on the PC we need to add the current user to the fuse group. This will grant the user  access to the mounted SSHFS partition
sudo usermod -a -G fuse halherta
  • The next step is to install Qt4’s meta object compiler known as ‘moc’ or ‘moc-qt4′ on the Linux PC. This compiler probes all the  header files that contain Qt4 object/class declarations and generates one additional source file (.moc.cpp) per header file (.h). These generated source files contain necessary Qt4  boilerplate code. The ‘moc-qt4′ tool generates source, not binaries. This means that its OK to use the ‘moc-qt4′ tool available on the PC in our cross-compiling setup.
  • Too install the ‘moc-qt4′ tool:
sudo apt-get install libqt4-dev-bin
  • Verify that the moc-qt4 tool is indeed installed by running:
moc-qt4 -v
  • If successful the output of the above command should be similar to:
Qt Meta Object Compiler version 63 (Qt 4.8.6)
  • Make sure that you are using the ‘moc-qt4′ tool. The Qt5 based ‘moc’ tool is NOT compatible with Qt4 builds.

Mount the RPi over SSHFS and prepare the PC for cross compilation of Qt4 apps

  •  Create a $HOME/rpi/mntrpi directory on your Linux PC.
  • Now mount the RPI’s root file system into $HOME/rpi/mntrpi on your PC over SSHFS by running the following command:
sudo sshfs pi@192.168.0.104:/ /home/halherta/rpi/mntrpi/ -o transform_symlinks -o allow_other

In order to use SSHFS for mounting you need to be root, hence the use of sudo preceding the command itself. After typing out the command, type the RPI’s username (pi) & IP address (192.1668.0.104) followed by a colon and the directory to be mounted. In this case we will mount the rpi’s root directory into   ‘/home/halherta/rpi/mntrpi/’ on our PC.  The other two flags do the following:

  • transform_symlinks – needed so that absolute symlinks in the RPI’s filesystem are transformed to relative ones. In this way a symlink on the RPI filesystem continue to point to the appropriate file on the RPI filesystem and not a similarly named file on the host system.
  •  allow_other - needed to allow all users (not just the root user) on the PC access to the RPi’s root filesystem
  • Now if we list the contents of ‘/home/halherta/rpi/mntrpi/’ we should see our entire RPI root filesystem in all of its glory.
  • On the Linux PC, create two soft links in the /lib && /usr/lib directories :
    • sudo ln -s  $HOME/rpi/mntrpi/usr/lib/arm-linux-gnueabihf/ /usr/lib/arm-linux-gnueabihf
    • sudo ln -s $HOME/rpi/mntrpi/lib/arm-linux-gnueabihf/ /lib/arm-linux-gnueabihf
  • Verify that the directory soft links indeed exist with the following two commands:
    • ls -ld /lib/arm-linux-gnueabihf
    • ls -ld /usr/lib/arm-linux-gnueabihf
  • Download the following compressed file containing source code for a simple Qt4 application and the makefile. qttest.tar.gz
  • untar the file in your home directory or in ‘/home/halherta/rpi’
    tar xvzf qttest.tar.gz

    Cross-compile the Qt4 Application

  • cd into the qttest directory and type ‘make’. Before typing ‘make’, be sure that the RPI’s filesystem is mounted via SSHFS! If it is not this step will fail!!!!! This is because the makefile expects  to find the RPI’s filesystem at ‘/home/halherta/rpi/mntrpi’. This step will also fail if the Qt4 libraries and tools were not installed properly as instructed earlier in this tutorial.
cross-compilation (building) of a simple QT4 application for the RPI on a PC

cross-compilation (building) of a simple QT4 application for the RPI on a PC

  • We can now move the application binary ‘target_bin’ into the RPI’s file system with a simple copy command:
    cp target_bin $HOME/rpi/mntrpi/home/pi
  • To unmount the SSHFS partition type:
sudo umount $HOME/rpi/mntrpi
  • Make sure that you unmount the SSHFS/RPI partition whenever its not needed, as it takes quite a lot of cycles/bandwidth to maintain this network partition.
  • We can now VNC into our RPI or login via SSH with X11 forwarding  and run the ‘target_bin’ binary.
  • The result is shown in the the figure displayed at the top of this blog entry.
  • The code example and makefile are listed below for convenience:

Makefile:

CXX=arm-linux-gnueabihf-g++

INCLUDEDIR = ./ 
INCLUDEDIR += $(HOME)/rpi/mntrpi/usr/include/qt4/
INCLUDEDIR += $(HOME)/rpi/mntrpi/usr/include/qt4/QtGui

LIBRARYDIR = $(HOME)/rpi/mntrpi/usr/lib/arm-linux-gnueabihf/
LIBRARY +=  QtCore QtGui 
XLINK_LIBDIR += $(HOME)/rpi/mntrpi/lib/arm-linux-gnueabihf
XLINK_LIBDIR += $(HOME)/rpi/mntrpi/usr/lib/arm-linux-gnueabihf

INCDIR  = $(patsubst %,-I%,$(INCLUDEDIR))
LIBDIR  = $(patsubst %,-L%,$(LIBRARYDIR))
LIB    = $(patsubst %,-l%,$(LIBRARY))
XLINKDIR = $(patsubst %,-Xlinker -rpath-link=%,$(XLINK_LIBDIR))

OPT = -O0
DEBUG = -g
WARN= -Wall
PTHREAD= -pthread

CXXFLAGS= $(OPT) $(DEBUG) $(WARN) $(INCDIR)
LDFLAGS= $(LIBDIR) $(LIB) $(XLINKDIR) $(PTHREAD)

INC = qttest.h 
SRC = main.cpp qttest.cpp

OBJ = $(SRC:.cpp=.o) $(INC:.h=.moc.o)

all: $(OBJ)
	$(CXX) $(LDFLAGS) $(OBJ) -o target_bin

%.moc.cpp: $(INC)
	moc-qt4  $<  -o $@

%.o:%.cpp
	$(CXX) $(CXXFLAGS)  -c $<  

clean:
	-rm *.o
	-rm target_bin

qttest.h (object/class declaration file)

#ifndef SLIDER_H
#define SLIDER_H

#include <QWidget>
#include <QSlider>
#include <QLabel>

class Slider : public QWidget
{
  Q_OBJECT

  public:
    Slider(QWidget *parent = 0);

  private:
    QSlider *slider; 
    QLabel *label;

};

#endif

qttest.cpp(object/class implementation file)

#include "qttest.h"

Slider::Slider(QWidget *parent)
    : QWidget(parent)
{ 
  slider = new QSlider(Qt::Horizontal , this);
  slider->setGeometry(50, 50, 130, 30);

  label = new QLabel("0", this);
  label->setGeometry(230, 50, 20, 30);

  connect(slider, SIGNAL(valueChanged(int)), label, SLOT(setNum(int)));
}

main.cpp (main)

#include <QApplication>
#include "qttest.h"

int main(int argc, char *argv[])
{
  QApplication app(argc, argv);  

  Slider window;

  window.move(300, 300);
  window.setWindowTitle("QSlider");
  window.show();

  return app.exec();
}

References and other helpful links:

This entry was posted in Qt, Raspberry Pi, Raspberry Pi Cross Development. Bookmark the permalink.

21 Responses to Cross Compiling Qt4 Applications for the Raspberry Pi

  1. Dave Thomas says:

    Is there any reason the makefile couldn’t be modified for Qt5 applications (assuming you get the RPI Qt5 libraries somehow)?

    • halherta says:

      Dave,
      I haven’t tried this with QT5. But I see no reason why it couldn’t be adapted for QT5. You’d have to make sure that you include the appropriate QT5 libraries instead of the QT4 ones and to use the appropriate QT5 ‘moc’ tool.

  2. Marcel says:

    Hi,
    is it possible to work with Qt Creator directly? When I want to create a arm-executable out of it it only creates a x86 executable.

    I think I need to customize the qmake, so it’ll point to the raspi includes/libraries. But how?

    Thanks and greetings,
    Marcel

    • halherta says:

      Marcel, Remember…qmake’s purpose is to create a makefile based on certain project information. Here we bypass qmake completely and build our own functional makefile. With some GNU Make/ Makefile knowledge this is a very plausible solution.

      Yes this is not a conventional approach at all…it’s very old school…but it works!. In fact I was able to use this approach to cross build Qt4 applications that relied on both the QWT and QtSerialPort libraries.

      This basically means that you can’t use QtCreator’s qmake wizardry unfortunately. I’m not sure if QtCreator supports a ‘custom makefile project’ but if such an option exists that would be your best bet….

  3. Steve Ferry says:

    Hi,

    Is there any way of cross compiling QT4 applications using a Windows PC or do I have to turn over a spare laptop to Linux ?

  4. Bart says:

    Thank you very much. Easy, painless and it just works.

  5. Dave Thomas says:

    I haven’t been able to find or install the deprecated ia32-libs required by all the other, older, cross-compilation tutorials I’ve read. This one works!

    But, I still need qtcreator…

    halherta… your reply to Marcella confuses me.

    “When I want to create a arm-executable out of it it only creates a x86 executable.” Isn’t the output of Qtcreator xml?

    • halherta says:

      Dave…I believe that Qtcreator outputs ‘some’ xml only for aspects controlled by the visual form editor. this is then translated to source and compiled into a binary in the build process. So ultimately you still need to compile or cross compile code into Arm or x86 binaries.

      I highly recommend that you forego using the visual Editor and build your application in pure code if you can…not only will get a much better understanding of Qt…but you’ll also have better control over your code.

  6. Dave Thomas says:

    I was thinking of the drag/drop feature of QtCreator. I definitely need that.

    But I don’t mind manually adding files to a .pro file.

    Suggestions?

    • halherta says:

      Dave,
      Unfortunately the approach described here doesn’t play well with QtCreator, nor qmake. Your best bet is to try and cross compile QT4/QT5 for Raspberry Pi from scratch. There are a few tutorials on how to do this on the Web. But I had no luck with the ones that I tried. Despite its flaws, the approach described here was what worked for me with the least amount of hassle.

  7. BoMann says:

    Hello,
    A really nice article.
    Is there not a cross compiler for Windows 8.1?
    I suppose I could install VM and the Ubuntu but I much prefer to stay on the windows line.

    Any help on this?
    Heaven would be Java AND C!

  8. Pingback: Cross Compiling for Embedded Devices | Miguel A. Nunes aka spacemig

  9. Brian says:

    Hello halherta,

    Thanks so much for this tutorial and the others (eclipse and raspberry pi). When I run the ‘make’ command in the qttest folder, I get the same messages as you, but then start getting other messages like:
    undefined reference to `QApplication::QApplication
    undefined reference to `QWidget::setWindowTitle

    and many more. Could you please point me to the step that I am missing?

    Thanks, Brian

    • halherta says:

      Brian, Based on these errors My guess is that the compiler is unable to view the necessary header files. Make sure that you can view the Pi’s root file system over SSHFS and make sure that the Makefile include/library directories are all valid. Then try again. If you can provide me with more details on your setup I might be able to help a bit more.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>