Cross compiling Qt/X11

Published Thursday September 10th, 2009
14 Comments on Cross compiling Qt/X11
Posted in Build system, Embedded, Qt

Overview

Cross compiling Qt Embedded is relatively trivial since it is so self
contained, but as the number of external dependencies increases (DirectFB,
D-Bus, GStreamer, OpenGL ES) adjusting your custom mkspec to individually
account for the location of dependencies on the host machine becomes
increasingly tedious/untidy. This complexity culminates in cross compiling
Qt/X11 which adds the additional dependency of fontconfig/FreeType/X11 to
the fray.

This blog is intended as a general overview of the cross
compilation of Qt/X11 for embedded Linux targets. The wide availability of
the BeagleBoard and the proliferation of embedded Linux platforms gives us
the rare opportunity (and impetus) to walk people through the cross
compilation of Qt for an X11 target and get it up and running on a
reference embedded device.

These steps can be summed up as:

  1. Build a reference Linux image and toolchain (Ångström)
  2. Create an appropriate Scratchbox 2 target
  3. Build Qt against this reference image
  4. Reap the whirlwind

This walk through will introduce you to 2 separate tools intended to
simplify cross compilation for embedded targets:

OpenEmbedded (used to build the reference Linux system (Ångström) and toolchain)
Scratchbox 2 (used to build Qt/X11 against the reference system)

They both have their strengths and their particular uses, and if I offend
your sensibilities by using them in conjunction be sure to express yourself
with vigour in the comments section below. Neither tool is required to
achieve this goal, both tools expedite achieving the desired goal.

Building Ångström

I have personally chosen to use Ångström as a reference platform as the
Open Embedded development environment is relatively straightforwards to
setup in a standard (K)Ubuntu Jaunty environment, supports both x86 and
x86_64 host machines, and because it provides a fairly minimalist Linux image
possessing everything required to build a full fledged version of Qt/X11
(X11, D-Bus, GStreamer, Glib, fontconfig, FreeType). Anyone from a Gentoo
background will also enjoy the reassuring quality of 9000 build steps
constructing your eventual Linux system from source.

I followed the instructions given here:

http://www.angstrom-distribution.org/building-ångström

which culminate in the following build step.

bitbake base-image ; bitbake console-image ; bitbake x11-image

I would personally recommend rather running:

bitbake beagleboard-demo-image

which builds an appealing E17 image and which meets all Qt/X11’s needs.

The resulting Linux images are immediately deployable and you will have
acquired a very recent (by cross compiler standards) gcc toolchain to use in
the next step

Creating an appropriate Scratchbox2 target

Scratchbox enables you to abstract away the host machine when cross
compiling for a target. Rather than having to correct for host paths, you
can simply execute certain commands in a chrooted environment which
directly resembles the desired targets layout with the targets headers/libs
in their anticipated locations. With Scratchbox 2, you simply prefix
toolchain calls with sb2, and they are run within the specified
realm/sandbox.

Firstly, install Scratchbox 2 using your distributions package manager, or
via whatever mechanism you prefer. Next, you will need to create a suitable
Scratchbox 2 target. This involves executing “sb2-init” (from within the
staging/root directory containing the target’s /usr/include and /usr/lib)
with the following arguments:

  1. an appropriate cpu transparency command (normally qemu-$arch)
  2. the desired name of the target
  3. the fully qualified path of the gcc compiler

My sb2-init line looked like this:

sb2-init -c /usr/bin/qemu-arm angstrom-beagle /OE/angstrom-dev/cross/armv7a/bin/arm-angstrom-linux-gnueabi-gcc

and was run within:

/OE/angstrom-dev/staging/armv7a-angstrom-linux-gnueabi;

(Tip: The targets you have created and associated parameters are helpfully
revealed in subsequent calls to sb2-init)

Prior to running sb2-init as shown above, I personally had to modify:

/usr/bin/sb2-build-libtool

by explicitly adding “–host=x86_64” to the configure line in order to get
libtool to compile successfully. The required diff would look something
like this (depending on your host):

– ./configure –prefix=$HOME/.scratchbox2/$TARGET –build=$(uname -m)-unknown-linux-gnu
+ ./configure –host=x86_64 –prefix=$HOME/.scratchbox2/$TARGET –build=$(uname -m)-unknown-linux-gnu

Cross compiling Qt/X11

Whenever you cross compile Qt you should create a custom mkspec based off
an appropriate reference mkspec, mkspecs/linux-g++ in this case. The
required modifications involve hijacking the qmake variables responsible
for defining all the target toolchain commands (gcc, g++, ar,
objcopy, strip) and prefixing each command with sb2.

My qmake.conf in my target mkspec looks like this:

=========================

#
# qmake configuration for linux-g++
#

MAKEFILE_GENERATOR = UNIX
TEMPLATE = app
CONFIG += qt warn_on release incremental link_prl
QT += core gui
QMAKE_INCREMENTAL_STYLE = sublib

include(../../common/g++.conf)
include(../../common/linux.conf)

# modifications to g++.conf
QMAKE_CC = sb2 gcc
QMAKE_CXX = sb2 g++
QMAKE_LINK = sb2 g++
QMAKE_LINK_SHLIB = sb2 g++

# modifications to linux.conf
QMAKE_AR = sb2 ar cqs
QMAKE_OBJCOPY = sb2 objcopy
QMAKE_STRIP = sb2 strip

load(qt_config)

=========================

Once you have modified this mkspec to address any additional considerations
you might have, you can export PKG_CONFIG_PATH to point to your targets
pkg-config (.pc) files, which are normally in a pkg-config directory under
the targets lib directory:

$targetPathOnHost/usr/lib/pkgconfig. Please note this path must be fully
qualified relative to the host)

Qt’s configure script currently does not employ pkg-config when
establishing FreeType library metainformation when testing for fontconfig
support, instead opting for heuristics which are explicitly excluded when
cross compiling. This rough little (unsupported) patch will resolve this,
and will result in the correct detection and compilation of Qt with
fontconfig/FreeType support.

=========================

diff –git a/configure b/configure
index 3f3e55a..54de96b 100755
— a/configure
+++ b/configure
@@ -5115,6 +5115,10 @@ if [ “$PLATFORM_X11” = “yes” ]; then
if [ -n “$PKG_CONFIG” ] && $PKG_CONFIG –exists fontconfig 2>/dev/null; then
QT_CFLAGS_FONTCONFIG=`$PKG_CONFIG –cflags fontconfig 2>/dev/null`
QT_LIBS_FONTCONFIG=`$PKG_CONFIG –libs fontconfig 2>/dev/null`
+ if $PKG_CONFIG –exists freetype2 2>/dev/null; then
+ QT_CFLAGS_FONTCONFIG=”$QT_CFLAGS_FONTCONFIG `$PKG_CONFIG –cflags freetype2 2>/dev/null`”
+ QT_LIBS_FONTCONFIG=”$QT_LIBS_FONTCONFIG `$PKG_CONFIG –libs freetype2 2>/dev/null`”
+ fi
else
QT_CFLAGS_FONTCONFIG=
QT_LIBS_FONTCONFIG=”-lfreetype -lfontconfig”

=========================

then kick off the configure with:

./configure -arch arm -xplatform $customMkspecDirName -force-pkg-config

configure, make and pkg-config are being run directly on the host,
pkg-config is using the targets .pc files, the core tools (qmake, uic, moc)
are being built for the host, the config.tests are being correctly built
against the targets mkspecs and the cross compiler is used for all target
specific compilation. With any luck you with see a large number of the
configure tests pass, and see Qt configured to be built with full
fontconfig/GStreamer/D-Bus support. You can now build Qt by simple running
“make” (You are using the host machines Make, and please note that you
never actually need to prefix sb2 on the commands you run, the qmake
generated Makefile will contain sb2 calls where applicable)

Reap the whirlwind

Cross compiling Qt applications now simply involves running the freshly
built qmake against the desired project .pro file, and running make as per
usual. (You are using Qt tools built for the host, and the host system’s
make and pkg-config applications amongst other things). Running make
therefore results in the host system’s “make” proceeding through all
the build steps using the correctly prefixed build tools.

Assuming your build is dynamic, your libraries and plugins have to be
deployed to the prefix path you either implicitly or explicitly specified
at configure time)

(Disclaimer: I realize that there is no implicit dependence on Linux when
discussing X11, and there is nothing Linux centric in my instructions other
than that it is the reference (and host) platform. I am building a standard
Qt X11 build in a controlled environment advertising my headers/libs in
their canonical paths (via Scratchbox) and making use of qmake’s cross
compiling infrastructure. If you have a suitable root filesystem,
associated toolchain (cross compiler), and if both Qt/X11 and Scratchbox 2
support your target platform, you should be set (although officially
thoroughly unsupported) to walk down the yellow brick road with these
instructions.)

****Note:  As of Qt 4.6 the above mentioned scratchbox2 approach is redundant on systems with a fully functioning pkg-config and is only recommended as a fall back. Build the Ångström sub-system, export PKG_CONFIG_PATH and PKG_CONFIG_SYSROOT appropriately and configure should correctly establish the capabilities of the sub-system.

Do you like this? Share it
Share on LinkedInGoogle+Share on FacebookTweet about this on Twitter

Posted in Build system, Embedded, Qt

14 comments

holger says:

Another way is to use the Angstrom Toolchain. Then using opkg-target to install the various -dev packages into the toolchain and then do. The Angstrom toolchain is using sysroot for both gcc and pkg-config so pkg-config can be nicely used for the target. One argument to do true is cross compilation is that you will not be hit by bugs in qemu, can optimize for architectures not supported by qemu and it is generally faster to execute moc/uic/qmake directly than to execute them with system emulation.

Some notes:

. /usr/local/angstrom/arm/environment-setup
opkg-target install libfontconfig-dev…
cd qt-version
./configure -force-pkg-config more options.

Another thing is some configure tests will break with “something gcc” due quoting, IIRC these are visibility detection and the reduce relocations one.

Eduardo Felipe says:

Nice post!

I’m trying to get the hang of QT Embedded on Beagleboard and I would very much appreciate your opinion: Do you think X11 is the best backend for QT when talking about a board that will be running a single application (Media center-ish). Or others, such as DirectFB or even OpenGL gives a smaller memory footprint overall?

Thanks so much!

dcarr says:

@holger

I am glad you made an appearance and are sticking your oar in. After you told me about OpenEmbedded, it took relatively little time for me to get Angstrom up and kicking (roughly 20 minutes from word go to kicking off the image build) and I was thoroughly impressed by how painless and intuitive it was to specify a machine and distro and churn out a whole Linux sub-system. I am frankly grateful for both OpenEmbedded and Scratchbox. The Qt-X11 recipes in the Angstrom repo looked a little long in the tooth, would you say it is equally trivial to get a full featured (passing all config.tests) Qt/X11 build up and going, or is major mkspec modification required? Would I be using OpenEmbedded’s pkg-config or the (possibly broken) one supplied by my distro? One thing I must correct though, using the above instructions all the Qt tools (moc/uic/qmake) are compiled for the host and hence nothing is run through qemu. The only executables that are run in the Scratchbox environment (are prefixed by sb2) belong to the actual cross compiler (native to the host) and hence qemu’s transparent execution should not actually be being used. To the best of my knowledge the only role of qemu in the scratchbox environment is the transparent execution of binaries which we are not making use of above. It would be simple enough to verify, and I will do so next time I am in the office.

@Eduardo

This is not a cut and dry problem, and it depends heavily on the hardware vendors and the hardware acceleration that they offer. If the BeagleBoard provided a decent hardware accelerated DirectFB driver, I would recommend (without hesitation) that you use Qt Embedded with DirectFB (The newer Qt, the better as DirectFB is being heavily worked on) for a single process media center-ish application. If the vendor provided a 2D accelerated X11 driver (or an X11 integrated OpenGL ES driver) I would tend to argue in favor of X11, but the other day I substantially increased the performance of the animated tiles animation example by explicitly using the Qt raster engine under X on the BeagleBoard. In circumstances like (Where Qt is doing all the heavy lifting in any case) this I would find it extremely difficult to justify using X for a single application system. I wish I had concrete cpu/memory benchmarks to provide empirical data steering one way or the other but at the moment I am afraid you will have to rely on hearsay and your better judgement. (I guess you could summarize my response as: “The Qt Raster Paint Engine + linuxfb > unaccelerated X” for single application systems)

FlunksterDude says:

Interesting read. Your directions make me feel like I actually could get this up and running. I have several cool ideas for little embedded pet-projects that I’ve never had the nerve to actually attempt.

dcarr says:

Please note, cutting and pasting my patches from above will probably be a patchy affair since several characters have been helpfully adjusted in blogging:

+ if $PKG_CONFIG –exists freetype2 2>/dev/null; then
+ QT_CFLAGS_FONTCONFIG=”$QT_CFLAGS_FONTCONFIG `$PKG_CONFIG –cflags freetype2 2>/dev/null`”
+ QT_LIBS_FONTCONFIG=”$QT_LIBS_FONTCONFIG `$PKG_CONFIG –libs freetype2 2>/dev/null`”
+ fi

The dashes above are actually double dashes, not 342200223, and beware the converted quotation marks

caveat emptor

dcarr says:

@holger: I have verified that the only binary that is executed for the target device (when using my above documented build method) is ldconfig, which I left out in the cold when disabling transparent target code execution:

/usr/bin/sb2: line 295: /OE/angstrom-dev/staging/armv7a-angstrom-linux-gnueabi/sbin/ldconfig: cannot execute binary file

The build still succeeded, and the resulting Qt tasted as good as ever. (Not half baked)

My scratchbox 2 env created for testing was created with:
sb2-init angstrom-beagle /OE/angstrom-dev/cross/armv7a/bin/arm-angstrom-linux-gnueabi-gcc

(I had deleted my original ~/.scratchbox2/angstrom-beagle)

I would still recommend using the original instructions, I just want to assure people that the only emulation overhead is that of running ldconfig for the target

Is it possible to compile Qt/X11 for Windows natively? Or is Qt/X11 only for UNIX?

Donald Carr says:

@King: Qt/X11 is only intended for compilation on UNIX systems. What a man does in the privacy of his own Cygwin environment is another story altogether. You might be able to get it working, but you would be fighting against incredible odds/infrastructure issues. Most people would be better off compiling Qt/Windows using MinGW in these circumstances. (And using msys, not cygwin! as our configure uses “sh” in the path as a primary identification factor)

Donald Carr says:

I have changed my approach to a path of less resistance. Gone is the “configure outside of scratchbox2, dive in when necessary” and surrounding complexities.

Now I:

1) Change to the target rootfs root dir
2) Create the Scratchbox 2 env
3) Go into the new Scratchbox 2 env (with sb2 -t $foo)
4) configure Qt with
– makespec set to use host-g++ and friends (builds tools for host)
– xmakespec set to linux-g++
– -force-pkg-config (we are still cross compiling and hence have to explicitly enable pkg-config use)

This gives you the best of all worlds:

1) Native tools
2) Fool proof automatic detection of functionality in rootfs
3) little chance of host contamination/symlink misery

FabioFerrario says:

I’m used to cross compile QT on eclipse for the AT91SAM9261 , it works great. Do you think a similar approach, so no X, basic Kernel and QT embedded, maybe cross compiled directly in QT Creator is a good approach for the BeagleBoard or should I stick to an X11 OE image?

Eero Tamminen says:

> One argument to do true is cross compilation is that you will not be hit by bugs in qemu

AFAIK SB2 can use also Sbrsh:
http://scratchbox.org/documentation/user/scratchbox-1.0/html/sbrsh.html

It can be used to transparently run the few necessary binaries on a real target device and it even works with (Debian) fakeroot. Only things it needs on the target device are working kernel with networking and minimal shell environment.

Sbrsh can be used with system qmu (which avoids threading and system call emulation related issues user-space qemu has).

Misha says:

@Donal, Could you provide an example on how you set the makespec and xmakespec settings for your setup?

Thank you

chlee says:

If I need to build a Qt/X11 for MIPS target, how to do?
Now, the scratchbox2 only supports ARM and X86 arch.
Can I use the scratchbox2 to build to it?

Greg says:

You’ve made some good points there. I checked on the net for more info about the issue and found most people will go along with your views on this web site.

Commenting closed.

Get started today with Qt Download now