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.