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.

35 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.

  10. Gilbert says:

    hello
    Really thank you for your site and the tutorials you share …. It is your tutorials that I have made great progress since the tutorial on Qt are not much on the net. I get to do a lot of cool stuff now on RPi after a cross compilation on a Debian desktop…
    I would ask you (and others as well!) Is that Qt4 is not made for the multimedia program ? I managed to display a main window with a few buttons. But I would like display a list of MP3 files and play the clicked file with omxplayer. I searched in the net but I have not found a single example. Perhaps with QtMultimedia? Could you recommend a site that will help me …
    thank you very much

    *** do not pay attention to my English (SVP), google translation was near me.

    • halherta says:

      Gilbert, In order to get mp3 playback going under QT4, you’ll have to use the Phonon libraries. QtMultimedia is only available under Qt5 unfortunately.

      • Gilbert says:

        Thank you for answering me and your advice. I’m going to start on this way.

        Merci beaucoup

        • Gilbert says:

          hello
          I’m sorry to ask you again because I can’t find how to solve the problem
          I get an error to Make:
          /home/gilou/rpi/tuto_phonon/mainwindow.cpp:7: undefined reference to `Phonon :: :: mediaobject mediaobject (QObject *) ‘
          /home/gilou/rpi/tuto_phonon/mainwindow.cpp:8: undefined reference to `Phonon :: :: AudioOutput AudioOutput (Phonon :: Category, QObject *) ‘
          /home/gilou/rpi/tuto_phonon/mainwindow.cpp:8: undefined reference to `Phonon :: CreatePath (Phonon :: MediaNode * Phonon :: MediaNode *) ‘
          /home/gilou/rpi/tuto_phonon/mainwindow.cpp:8: undefined reference to `Phonon :: Path :: ~ Path () ‘
          /home/gilou/rpi/tuto_phonon/mainwindow.cpp:9: undefined reference to `Phonon :: MediaSource :: MediaSource (const & QUrl) ‘
          /home/gilou/rpi/tuto_phonon/mainwindow.cpp:9: undefined reference to `Phonon :: :: mediaobject setCurrentSource (const Phonon :: MediaSource &) ‘
          /home/gilou/rpi/tuto_phonon/mainwindow.cpp:9: undefined reference to `Phonon :: MediaSource :: MediaSource ~ () ‘
          /home/gilou/rpi/tuto_phonon/mainwindow.cpp:10: undefined reference to `Phonon :: :: mediaobject play () ‘
          /home/rivo/gilou/tuto_phonon/mainwindow.cpp:9: undefined reference to `Phonon :: MediaSource :: MediaSource ~ () ‘
          collect2: error: ld returned 1 exit status
          make: *** [all] Error 1
          rm mainwindow.moc.cpp

          I add into Makefile
          INCLUDEDIR += $ (HOME) / rpi / mntrpi / usr / include / phonon
          where the files exist but make still wan’t ….

          Does anyone have an idea?

          Thank you for your help

          • halherta says:

            Gilbert not sure about about Phonon but perhaps you need to add the location of the phonon library to the LIBRARYDIR variable and the phonon library to the LIBRARY variable as well.

            You’ve probably done this already…but in case you didn’t make sure you install phonon libraries on the RPI.

  11. Dave Thomas says:

    These instructions worked well, but I really want to continue using the QtCreator IDE.

    I don’t understand why the compiling machines QtCreator and Qmake can be used. Can’t qmake be configured to create a makefile like you’ve done manually?

    I’m not that familiar, but based on other online sources, it seems like that’s what’s commonly done for cross-compiling using qtcreator. But, before I dive into that, I want to make sure I’m not missing something–certainly possible.

  12. Dave Thomas says:

    Meant to say:

    I don’t understand why the compiling machines QtCreator and Qmake can NOT be used.

    • halherta says:

      Dave,
      It is possible to use QtCreator and QMake in a cross compiling setup. But in order to configure this you’ll have to build the Qt Libs & QMake from source using the RPi’s cross compiler. This while possible can be a pain and failed many times when I attempted it. Others seemed to have succeeded however.

      My goal was to come up with a solution that would use the existing Qt4 libs available on the RPi (in the Raspbian repos)

      • Dave Thomas says:

        If I already have the Qt libs on the raspberry pi (which is mounted per your instructions), do I really have to build them from source using a cross compiler?

        That’s the part I don’t understand.

        Thanks!

        • halherta says:

          Based on my understanding (which could be wrong or just outdated) QMake and QtLibs have to be built together in a cross compiling setup so that the QMake binary is able to manage the cross build. If I’m wrong about this please let me know.

          In any case by modifying the Makefile appropriately I was able to build Qt4 apps that relied on the QWT and QSerialPort libraries. I built both libraries on the RPi and simply appended their ‘include’ and ‘lib’ directories to the Makefile. I was also able to have a slightly better understanding of how QMake builds Qt4 apps. The only thing that I missed was QtCreator’s awesomeness i.e. (interface, ease of use, GUI Wizard, ‘intellisense’, documentation e.t.c)

          BTW have you looked at this?

          I attempted these instructions but failed miserably. But that was more than a year ago now.

          • Dave Thomas says:

            I’m playing with QMAKESPECS, seems like that should be sufficient to get a qmake with uses the correct toolchain. And, there seem to be qmake persistent variables to do the stuff in your custom makefile.

            However, I think the QtCreator build configuration GUI might have some issues, at least I haven’t been able to make it “happy” with my “special” qmake and the arm toolchain.

            I’m still playing with it and asking elsewhere, so I’ll post back here what I find out.

            Thanks!

  13. Dave Thomas says:

    I got qmake and QtCreator working on a host Ubunto (in a vmplayer on Windows 7) It cross compiles qt4 applications for raspberry pi.

    There’s no need to rebuild qmake. It has all the configuration options you need.

    I followed this tutorial then just used created a mkspecs file for qmake that implements what Halherta did in his custom makefile. This way, I can use the QtCreator IDE including deployment, source control, etc.

    There are probably lots of ways to do this, but I just created a linux-arm-g++ directory in /usr/share/qt4/mkspecs. Starting place was a copy of the linux-g++ directory, then I edited the qmake.conf in that directory using Halherta’s makefile as a guide.

    Here’s what it looks like (my userid is davethomaspilot on the host machine):

    #
    # qmake configuration for linux-arm-g++
    #

    CROSS_COMPILE = arm-linux-gnueabihf
    ROOTFS = /home/davethomaspilot/rpi/mntrpi
    MAKEFILE_GENERATOR = UNIX
    TARGET_PLATFORM = unix
    TEMPLATE = app
    CONFIG += qt warn_on release incremental link_prl gdb_dwarf_$
    QT += core gui
    QMAKE_INCREMENTAL_STYLE = sublib

    include(../common/linux.conf)
    include(../common/gcc-base-unix.conf)
    include(../common/g++-unix.conf)

    QMAKE_CC = $$CROSS_COMPILE-gcc
    QMAKE_CXX = $$CROSS_COMPILE-g++
    QMAKE_LINK = $$CROSS_COMPILE-g++
    QMAKE_LINK_SHLIB = $$CROSS_COMPILE-g++
    QMAKE_AR = $$CROSS_COMPILE-ar cr
    QMAKE_OBJCOPY = $$CROSS_COMPILE-objcopy
    QMAKE_STRIP = $$CROSS_COMPILE-strip
    QMAKE_LFLAGS_RELEASE = -Wl,-O1

    QMAKE_RPATHDIR += $$ROOTFS/lib/arm-linux-gnueabihf
    QMAKE_RPATHDIR += $$ROOTFS/usr/lib/arm-linux-gnueabihf
    QMAKE_INCDIR = $$ROOTFS/usr/include
    QMAKE_INCDIR_QT = $$ROOTFS/usr/include/qt4
    QMAKE_RPATHDIR = $$ROOTFS/lib/arm-linux-gnueabihf
    QMAKE_RPATHDIR += $$ROOTFS/usr/lib/arm-linux-gnueabihf
    QMAKE_LIBDIR = $$ROOTFS/usr/lib
    QMAKE_LIBDIR_QT = $$ROOTFS/usr/lib/arm-linux-gnueabihf
    QMAKE_LIBDIR_QT += $$ROOTFS/lib/arm-linux-gnueabihf
    QMAKE_INCDIR_X11 = $$ROOTFS/usr/include
    QMAKE_LIBDIR_X11 = $$ROOTFS/usr/lib/arm-linux-gnueabihf
    QMAKE_LIBDIR_X11 += $$ROOTFS/lib/arm-linux-gnueabihf
    QMAKE_INCDIR_OPENGL = $$ROOTFS/usr/include
    QMAKE_LIBDIR_OPENGL = $$ROOTFS/usr/lib

    load(qt_config)

    Then, in qtcreator, on the build settings page, beside qmake click “detail” beside the build configuration . Then, click on additional arguments and add:

    -spec linux-arm-g++

    This flag loads the associated qmake.conf.

    If you want to deployment options to work, then you need to edit the .pro file and add these lines (this example is for the target binary go into a directory on the pi with the same name):

    target.path = /home/pi/qttest
    INSTALLS += target

    And, under Run settings you’ll need to add a deploy step. Pretty obvious, I think, but check the qtcreator docs if you have problems.

    I’d like to get remote debugging working too, so I’ll try that next.

    • halherta says:

      Thanks Dave! I’ll give this a try when I have some free time! Please do keep me posted on remote Debugging as well.

      • Dave Thomas says:

        I got remote debugging working, but I had to launch gdbserver on the target machine before starting each debug session.

        I had a running thread on this on qtcentre and asked if it was possible to configure qtcreator to launch gdbserver on the remote target automatically. The answer was yes, but I was advised to move up to a newer version of qtcreator using this link:

        http://www.qt.io/download-open-source/#section-6

        I did that–the newer version seems much more responsive. I’ve configured it to build using the same -spec argument to qmake, but so far I haven’t gotten remote debugging working on the new qtcreator yet.

        I’ll post back when that’s working.

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>