Monday, August 22, 2011

Developping for Qt 4.8 Embedded on Windows

"Yes we can!" [Obama '08]

I started developing with Qt for Embedded Linux 5 years ago (it was Qt 3.3.1 at the time) and I immediately fell in love with the "hands in the dirt" aspect of embedded programming! However, I have to confess that as much as I love embedded stuff, I'm still a Windows guy... (no judgements please). Actually, while in my case it's purely a matter of habits, many people are stuck with Windows as their mandatory development platform, e.g. due to company policies, Windows-only software/SCM...

So, one day while downloading Sourcery-G++, the popular gcc toolchain for ARM, I noticed that a Windows version was available as well. Out of curiosity I installed it, and just as I hoped what I got was a full-fledged ARM toolchain for Windows, with gcc, gdb, a sysroot, etc.

The first thing that then came to my mind was:

Will it blend^W be possible to cross-compile Qt 4.8?

Well, yes it certainly is possible, and it took surprisingly little effort to get there. This is mainly thanks to qmake which provides a lot of the support out-of-the-box.
And it gets even better: Using the upcoming Qt Creator 2.3.0 you can also cross-compile your applications and even debug them remotely, all within the IDE!

Want to give it a try too? Here's how:

1) What you'll need

2) What you won't need
  • Cygwin, MSYS, or any other linux shell/emulation layer. The standard command line can be used

3) Installing the tools

Follow the instructions to download and install both the host and target toolchains. The installers should make it only a couple of clicks. You may want to add the toolchains permanently in your PATH for convenience, but this is not necessary.

Optionally, install Jom and add it to your PATH.

4) Downloading the Qt source

In the directory where you installed Git for Windows, double-click on the file git-cmd.bat. This opens a command line environment with the git tools set up. Navigate to the place where you want the Qt source downloaded, e.g. C:\Dev\, then clone the Qt source repository from gitorious:
C:\Dev>git clone git://qt.gitorious.org/qt.git qt-4.8-src

Once the download is done, let's switch to the 4.8 branch:
C:\Dev>cd qt-4.8-src
C:\Dev\qt-4.8-src>git checkout -b 4.8 origin/4.8


5) Build preparations

Before going ahead with the build itself, we need to patch qmake.exe and configure.exe to add a few missing things and fix a couple of bugs. The patches are pending upstream and may be merged in soon, so hopefully this step won't be necessary by the time Qt 4.8.0 is released:

For qmake.exe: merge request or patch1 and patch2
For configure.exe: merge request or patch1 and patch2

Typically, Qt source checkouts come with a prebuilt configure.exe, and qmake gets built during the configuration step. However, we do need qmake to build the patched configure.exe, so we're in a chicken-and-egg situation. Not a big problem though, we could run configure.exe once first to build the patched qmake.exe, then rebuild configure.exe, then re-run it for the actual build. But a simpler alternative is to use another qmake.exe to rebuild configure.exe if you already have a Qt installed on your machine (any version >= 4.6.0 is fine):

Open a Qt Command Prompt from the Windows Start menu, and navigate to the Qt 4.8 checkout, then do:
C:\Dev\qt-4.8-src>cd tools\configure
C:\Dev\qt-4.8-src\tools\configure>qmake && nmake

(replace nmake with mingw32-make if Qt was built for MinGW)

This will rebuild configure.exe and automatically replace the one in the top-level directory. Note that you can even use Qt Creator to rebuild configure.exe: just open C:\Dev\qt-4.8-src\tools\configure\configure.pro, then click the "Build" button and that's it.

6) Setting up the target Makespec

We're now almost ready to run the configure.exe script, but we have one more thing to do before that.

An important aspect of configuring Qt is to select the proper Makespec that qmake will use. A Makespec is a file that specifies what toolchain to use, the target platform, the compiler and linker flags, the standard libraries to link, and so on. In the case of cross-compiling, we have to specify two Makespecs: one for the host platform (the platform we're building on, ie. Windows) and one for the target (the platform we're building for, ie. ARM/Linux).

Qt comes with many predefined Makespecs, found under the mkspecs/ directory. Now, Qt doesn't provide target Makespecs when cross-compiling on Windows, so we'll need to provide our own instead (note that we can't use the ones provided for cross-compiling on Linux, since those are made for compiling in a linux shell environment. They're still useful as a base to see what needs to be set though). So this is the Makespec I've written (also available here):


#
# qmake configuration for building with arm-none-linux-gnueabi-g++
#
include(../common/unix.conf)
include(../common/gcc-base-unix.conf)
include(../common/g++-unix.conf)
include(../common/qws.conf)

MAKEFILE_GENERATOR      = MINGW
CONFIG                 += no_import_libs no_generated_target_info
# modifications to g++.conf
QMAKE_CC                = arm-none-linux-gnueabi-gcc
QMAKE_CXX               = arm-none-linux-gnueabi-g++
QMAKE_LINK              = arm-none-linux-gnueabi-g++
QMAKE_LINK_SHLIB        = arm-none-linux-gnueabi-g++
QMAKE_LIB               = arm-none-linux-gnueabi-ar
QMAKE_AR                = arm-none-linux-gnueabi-ar cqs
QMAKE_OBJCOPY           = arm-none-linux-gnueabi-objcopy
QMAKE_STRIP             = arm-none-linux-gnueabi-strip
QMAKE_RUN_CC            = $(CC) -c $(CFLAGS) $(INCPATH) -o $obj $src
QMAKE_RUN_CC_IMP        = $(CC) -c $(CFLAGS) $(INCPATH) -o $@ $<
QMAKE_RUN_CXX           = $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $obj $src
QMAKE_RUN_CXX_IMP       = $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
QMAKE_INCDIR            =
QMAKE_INCDIR_QT         = $$[QT_INSTALL_HEADERS]
QMAKE_LIBDIR_QT         = $$[QT_INSTALL_LIBS]
QMAKE_MOC               = $$[QT_INSTALL_BINS]\\moc.exe
QMAKE_UIC               = $$[QT_INSTALL_BINS]\\uic.exe
QMAKE_IDC               = $$[QT_INSTALL_BINS]\\idc.exe
QMAKE_COPY              = copy /y
QMAKE_COPY_DIR          = xcopy /s /q /y /i
QMAKE_MOVE              = move
QMAKE_DEL_FILE          = del
QMAKE_MKDIR             = mkdir
QMAKE_DEL_DIR           = rmdir
QMAKE_CHK_DIR_EXISTS    = if not exist
QMAKE_IDL               = midl
QMAKE_ZIP               = zip -r -9
CODESOURCERY_ARM_CFLAGS = -march=armv7-a -mtune=cortex-a8 -mthumb -mfpu=neon -mfloat-abi=softfp -Wa,-mimplicit-it=thumb
TARGET_SYSROOT          = /CodeSourcery-sysroot
TARGET_QTDIR            = /QtEmbedded-4.8.0-arm
#modifications to gcc-base.conf
QMAKE_CFLAGS           += $$CODESOURCERY_ARM_CFLAGS
QMAKE_CXXFLAGS         += $$CODESOURCERY_ARM_CFLAGS
QMAKE_LIBS             += -lrt -lpthread -ldl
QMAKE_LFLAGS           += $${QMAKE_LFLAGS_RPATH}$$[QT_INSTALL_LIBS]
!isEmpty(TARGET_QTDIR) {
    QMAKE_LFLAGS       += $${QMAKE_LFLAGS_RPATH}$${TARGET_QTDIR}/lib
}
!isEmpty(TARGET_SYSROOT) {
    QMAKE_LFLAGS       += $${QMAKE_LFLAGS_RPATH}$${TARGET_SYSROOT}/lib:$${TARGET_SYSROOT}/usr/lib
    QMAKE_LFLAGS       += -Wl,--dynamic-linker=$${TARGET_SYSROOT}/lib/ld-linux.so.3
}
load(qt_config)


As you can see, we use the MINGW makefile generator since it supports gcc commands lines (which is what Sourcery-G++ is based on). Most of the work then consists in specifying the executables and compiler/linker options.

For the compiler options I'm assuming we're building for an ARM Cortex-A8 CPU, as found on the BeagleBoard. Depending on your CPU type and architecture, the compiler options might differ.

Also, the TARGET_SYSROOT and TARGET_QTDIR variables only need to be set if your target OS wasn't build with Sourcery-G++, or if it uses a different version, and if the Qt libraries aren't going to be in a standard path. More details about that further down.

Save this to a file called qmake.conf in a subdirectory of mkspecs. This directory will be the name of your Makespec:
C:\Dev\qt-4.8-src\mkspecs\linux-arm-gnueabi-g++\qmake.conf

You also need a file called qplatformdefs.h, which we can copy directly from the Linux Makespec in qws/linux-arm-gnueabi-g++:
C:\Dev\qt-4.8-src\mkspecs\linux-arm-gnueabi-g++>copy ..\qws\linux-arm-gnueabi-g++\qplatformdefs.h

7) Configuring Qt

We're now ready to run configure.exe, which will create qmake, set up the build-time variables and build configuration, then create the Makefiles for the Qt libraries and examples.

Open a command prompt with the host toolchain set up (ie. MinGW or Visual Studio), then go to the Qt source directory. This is the minimal configure.exe command line we need to enter:

C:\Dev\qt-4.8-src>configure.exe -debug -embedded -arch arm -neon -platform win32-msvc2010 -xplatform linux-arm-gnueabi-g++

The options are as following:

-debug: we want a non-optimized build with debug symbols. You could use -release instead for a optimized build without debug symbols.
-embedded: means we want to build the embedded version of Qt, not the desktop version. This activates the use of QWS and other features specific to Qt Embedded.
-arch arm: we're building for an ARM CPU. This is used internally to select the proper implementations for low-level stuff.
-neon: We want to make use of the NEON co-processor SIMD instructions, which considerably improves painting performance. However note that NEON is only available for Cortex-A8 and later ARM CPUs.
-platform win32-msvc2010: this is the host Makespec, ie. the one we'll use to build qmake, moc, uic, and other tools running on the host. In this case we're using Visual Studio 2010, but you could replace it with win32-g++ if you want to use MinGW instead.
-xplatform linux-arm-gnueabi-g++: this is the target Makespec, ie. the one we'll use to build libraries, tools, and examples. This will use the CodeSourcery compiler and linker.

You can of course add more options to it, for example to disable building some modules (like Qt3Support, Phonon, or Webkit) or to even skip building the additional tools like Assistant, Designer, etc., since those are useless on the target platform. I usually also skip building the examples and demos, since those increase the build time considerably and I never use them.

This is my typical command line:
C:\Dev\qt-4.8-src>configure.exe -debug -embedded -arch arm -neon -platform win32-msvc2010 -xplatform linux-arm-gnueabi-g++ -fast -no-phonon -no-webkit -no-qt3support -nomake tools -nomake translations -nomake examples -nomake demos

Normally the program should complete without errors. If you did get some, make sure your host toolchain is properly set up (tools in PATH, INCLUDE and LIB set if you're using Visual Studio), and that the proper makespec and architecture are selected (you can see it in the summary printed out by configure.exe before the makefiles are generated).

8) Cross-compiling Qt

The final step is to build Qt, which is as simple as running your make tool in the top-level build folder (there should be a Makefile in there now).

Note however that if you used Visual Studio as the host toolchain, do NOT use nmake to build Qt, as the target makefiles are generated for MinGW instead.

I recommend just using Jom, no matter which host toolchain you used. Jom handles both mingw and msvc makefiles, with -j support for parallel builds, and it's much faster than mingw32-make.

3) Profit

So now is a good time to get a cofee and take a break! Depending on your machine and what modules you're building, this may take up to four hours for the build to complete. Also, Sourcery-G++ on Windows is substantially slower than the MSVC compiler or even MinGW, so don't be surprised if it seems slower than normal.

Once the build is done, you'll find the Qt libraries as .so files in the lib/ directory, and the examples and demos in their respective directories if you built those.

The libraries and executables can be copied directly onto your target board and run from there. If the toolchain matches the toolchain used for building your board image (release and machine versions), it will work out of the box. Otherwise you'll need to copy the sysroot from Sourcery-G++ as well on the board and make sure the app can find it. Note that you can use the sysroot from the linux installers instead of copying it from windows, which is a little easier to deploy. More details about sysroot handling with Sourcery-G++ here.


...and that's it!

In the next post, I will show you how to cross-compile your own Qt applications as well, and how to use the new features in QtCreator 2.3 to cross-compile within the IDE, deploy the applications on the board, and even do remote debugging, all from your Windows desktop!

16 comments:

  1. Haha, they didn't provide the Mac version installer like they did for Symbian.

    Maybe someone could have a try to build it from source.

    ReplyDelete
  2. Hi Kromain, very useful post.
    I wish I could find it before.

    ... are you planning to post "how to cross-compile your own Qt applications as well" for Qt 4.8 Embedded on Windows?

    Thx

    ReplyDelete
  3. Anonymous: Thanks!
    Yes I have started working on the next part of the blog, based on Qt Creator 2.4, so it should be out soon.

    Actually I'm going to rewrite this post as well, since some things have changed in the meantime, and since this was still using the old QWS architecture. The new post will present the new QPA one instead. Stay tuned! (and patient :))

    ReplyDelete
  4. Hello Romain,
    and thank you for this post. I'm trying to setup QT embedded arm linux SDK on Windows and it seems that you are the only one who deals with it. I have used your patches to officially released 4.8.0 sources and now I'm able to configure and compile these sources on Windows. I have just one question - qmake generates only one Makefile and no two additional for release and debug like I'm used from linux. Do you know why? I also was not able to take the compiled qt and use it on another PC in different location - it seems that some paths are absolute and hardcoded. Is this normal behaviour?

    thanks and regards
    Jan

    ReplyDelete
    Replies
    1. Hi Jan,

      Good to hear this was useful to you!
      Normally the separate Makefiles are used for debug-and-release builds, and is a Windows thing only. It is not supported on Linux anymore, as the configure script states:

      "WARNING: -debug-and-release is not supported anymore on Qt/X11 and Qt for Embedded Linux.
      Qt can be built in release mode with separate debug information, so -debug-and-release is not necessary anymore"

      Regarding copying the Qt install to another PC, yes that is a well-known -and unfortunate- misfeature. It is possible to work around it in most cases though, using a qt.conf file (see http://developer.qt.nokia.com/doc/qt-4.8/qt-conf.html)

      Cheers
      Romain

      Delete
  5. Hi Romain,
    First, I would like to thank you for the useful info, I am breaking my head with this issue for a few days already. I am trying to develop a GUI app for PandaBoard and I cant find a correct makespec.
    Anyway, I have a novice question, why didnt you use the Windows binary and decided to compile the version?

    ReplyDelete
  6. Hey Romain,

    Thank you for this information, this post was extremely helpful to me.

    I cannot seem to find your secondary post for deploying and debugging to target board from inside Qt Creator. Could you please point me to it?

    Thanks,
    Phil

    ReplyDelete
  7. Hi Romain,
    I followed all yours instructions and all is right but when i try to compile the libraries in release instead of debug I get an internal gcc error (segmentation fault). Hove you tried to do that ?

    ReplyDelete
  8. Hi Roman,
    sorry but your links for qmake.exe and configure.exe doesn't work. Can you post your qmake.exe. I can't build it with configure.bat.

    Thank you

    ReplyDelete
    Replies
    1. Hi,

      Yeah the pastee links are long gone unfortunately. You can still find the patches as part of the merge requests, for which the links are still valid (https://qt.gitorious.org/qt/qt/merge_requests/1329 and https://qt.gitorious.org/qt/qt/merge_requests/1321)

      Also, just to clarify: the links you tried to access only contained patches to build configure.exe and qmake.exe, not the executables themselves. I don't have those handy anymore either unfortunately.

      Delete
  9. Hi Roman,

    Great work. I used it at my last company. Since gitorious merge requests is no longer available and the pastee are gone. Is there is place to get the patches?

    Thanks

    ReplyDelete
    Replies
    1. Darn, I'm afraid you're out of luck then :(

      However, if you can work with Qt 5.x I think you won't need those patches anymore, and the rest of the instructions should still apply more or less the same (you might just have to adjust the paths to the makespecs folder, and some configure.exe options might have changed too). Unfortunately I don't have a Windows setup anymore myself so I can't tell for sure, sorry about that.

      Anyway, thanks for reaching out and I'm glad to hear this was useful to you even after all these years!

      Delete
    2. I found that you don't need the patch if you set MAKEFILE_GENERATOR = UNIX in the qmake.conf.

      Delete
    3. Also add QT_LIBINFIX = E to qmake.conf file so the E suffix is added to library filenames in generated Makefiles. With MAKEFILE_GENERATOR = UNIX you don't have to build qmake.exe, just use the one that comes with the Qt installation for Windows.

      Delete
  10. http://web.archive.org/web/*/http://qt.gitorious.org/qt/qt/merge_requests/1329

    you got the ideea

    ReplyDelete
  11. Hello, thanks for this article. I am tring to compile qt4.8.6 but everytime I am getting the same error and I could't manage with it. Any help is wellcome.

    arm-linux-gnueabihf-g++ -c -pipe -g -Wall -W -D_REENTRANT -fPIC -DQT_SHARED -DQT
    _NO_GLIB@ -DQT_BUILD_GUI_LIB -DQT_NO_USING_NAMESPACE -DQT_NO_CAST_TO_ASCII -DQT_
    ASCII_CAST_WARNINGS -DQT_MOC_COMPAT -DQT_USE_QSTRINGBUILDER -DQT_USE_BUNDLED_LIB
    PNG -DPNG_NO_ASSEMBLER_CODE -DQT_NO_OPENTYPE -DQT_NO_STYLE_MAC -DQT_NO_STYLE_WIN
    DOWSVISTA -DQT_NO_STYLE_WINDOWSXP -DQT_NO_STYLE_WINDOWSCE -DQT_NO_STYLE_WINDOWSM
    OBILE -DQT_NO_STYLE_S60 -DQ_INTERNAL_QAPP_SRC -DQT_HAVE_NEON -D_LARGEFILE64_SOUR
    CE -D_LARGEFILE_SOURCE -DQT_CORE_LIB -I../../mkspecs/qws/linux-arm-gnueabi-g++ -
    I. -I../../include/QtCore -I../../include -I../../include/QtGui -Itmp/rcc/debug_
    shared -I../3rdparty/xorg -Iimage -I../3rdparty/libpng -I../3rdparty/zlib -I../3
    rdparty/zlib -I../3rdparty/harfbuzz/src -Idialogs -Itmp/moc/debug_shared -I. -o
    tmp/obj/debug_shared/qapplication_x11.o kernel/qapplication_x11.cpp
    :0:11: warning: missing whitespace after the macro name [enabled b
    y default]
    In file included from kernel/qapplication_x11.cpp:93:0:
    ../3rdparty/xorg/wacomcfg.h:26:22: fatal error: X11/Xlib.h: No such file or dire
    ctory
    #include
    ^
    compilation terminated.
    Makefile:64069: recipe for target 'tmp/obj/debug_shared/qapplication_x11.o' fail
    ed
    mingw32-make[3]: *** [tmp/obj/debug_shared/qapplication_x11.o] Error 1
    mingw32-make[3]: Leaving directory 'c:/Qt/qt-everywhere-opensource-src-4.8.6/src
    /gui'
    Makefile:2: recipe for target 'all' failed
    mingw32-make[2]: *** [all] Error 2
    mingw32-make[2]: Leaving directory 'c:/Qt/qt-everywhere-opensource-src-4.8.6/src
    /gui'
    Makefile:381: recipe for target 'sub-gui-make_default-ordered' failed
    mingw32-make[1]: *** [sub-gui-make_default-ordered] Error 2
    mingw32-make[1]: Leaving directory 'C:/Qt/qt-everywhere-opensource-src-4.8.6'
    Makefile:2: recipe for target 'all' failed
    mingw32-make: *** [all] Error 2

    ReplyDelete