2007-09-11

Cross-compiling Qt4/Win on Linux

I've recently been playing rediscovering Qt with Qt4. Qt is a great cross-platform programming environment for GUI apps—the best I've used, at least. Anyway, as I put my app together, I thought that I should release it as a windows binary as well—both to use (and test!) this Qt cross-platform compatibility and because I expect the majority of the tool users would be windows users. Alas, I don't have a working windows os (I do have installed some 120-day evaluation version of win2003 somewhere, but even if it works, which I doubt, given some hardware changes since I've last launched it, I think the evaluation period is over anyway), let alone windows development environment.

So, I immediately thought about cross-compiling. Cross-compiling is compiling for another target platform than the host platform (where we run compilation). Generally, mingw32-gcc works marvels for linux-to-win32 cross-compilation and I've been using it some time ago for GTK apps. That was easier, because GTK/win is available in compiled form, so I just needed to cross-compile my own apps. For Qt4/win, no mingw32 binaries are readily available. So I had to take a go at compiling it myself.

After some googling, I've found that although some people tried to achieve that, none of them succeeded. This turned me down a little bit, but I'm not a person which would run away from a challenge.

The main problem with this cross-compilation is Qt4 build system. Although it seems to support cross-compilation, it really can only do it out-of-the-box between different Unix platforms. Since mingw32 is not strictly a Unix platform and different codepaths need to be compiled, this approach fails. So what we need to do is trick the buildsystem into thinking that win32 codepaths apply to unix.

Without further ado, I present you with step-by-step instructions on how to cross-compile Qt4/win. First, though, you need to obtain cross-compiling environment. On gentoo it's simple. Run the following as root:
emerge crossdev
crossdev -t mingw32

Well, simple as it may be, compilation was broken on my box on one step. It seems that it didn't find the right compiler to compile win32api. What was needed is
gcc-config mingw32-4.2.0
source /etc/profile

After that I've re-issued the crossdev -t mingw32 command and everything worked.

Now for the compilation of Qt4/win. You need to have Qt4/X11 already installed on your system, as we'll use Qt utilities already available on the system. Make sure you don't the following as root, because the build system for some reason insists on installing DLLs in /usr/bin (doesn't break if it can't, so no harm).

  1. Get Qt4/win sources. I've used 4.3.1 edition.
  2. Unpack them. Enter the directory.
  3. Trick the buildsystem into using win32 feature files:
    cd mkspecs/features
    ln -s win32 unix
    cd ../..
  4. Download the patch. Apply it.
    patch -p1 < qt4win-cross-mingw.patch
  5. Make sure qmake doesn't use compilation paths meant for unix
    find src -name '*.pro' -o -name '*.pri' | xargs sed -i -e 's/\(^\|[^_/]\)unix/\1linux/g;'
  6. Make qmake use compilation paths meant for Windows.
    find src -name '*.pro' -o -name '*.pri' | xargs sed -i -e 's/\(^\|[^_/]\)win32\([^-]\|$\)/\1unix\2/g;'
  7. Trick configure into using system utilities. for f in moc rcc uic qmake; do ln -s `which $f` bin; done
  8. Make sure we don't build them:
    echo qmake: > qmake/Makefile.unix
    for f in `ls src/tools`; do echo TEMPLATE = subdirs > src/tools/$f/$f.pro; done
  9. We need some files from the Qt/X11 source, specifically configure script and config.tests directory. I've got them from my qt-copy (from KDE repo); you can use that or download Qt/X11 sources. Put them in the main source directory.
  10. Configure. These options are carefully chosen not to break compilation (except -prefix and -prefix-install; the latter doesn't seem to work anyway).
    ./configure -prefix $PWD -xplatform win32-g++ -no-largefile -exceptions -no-accessibility -no-qt3support -make libs -prefix-install $PWD -no-reduce-exports
  11. Make a missing makefile.
    qmake -spec $PWD/mkspecs/win32-g++ src/winmain/winmain.pro -o src/winmain/Makefile
  12. Build.
    make
  13. Done!

If you have Wine, you can even test it. Just make sure the dlls are where Wine can find them: cp /usr/mingw32/usr/bin/mingwm10.dll lib/*.dll /home/divide/.wine/c/windows/system/. To compile the demos, do
cd demos
qmake -spec ../mkspecs/win32-g++ demos.pro
make
wine demos/affine/affine.exe

A screenshot of affine Qt4 demo under wine

The only problem I've found with using Qt4 in wine is that the latter doesn't implement some font handling functions and it sometimes breaks font rendering where text is laid out dynamically—esp. QGraphicsView and QTextEdit seem to be the worst offenders.

12 comments:

Jaak Ristioja said...

Errors:

qt-win-opensource-src-4.3.1 $ patch -p1 < ../qt4win-cross-mingw.patch
patching file src/gui/inputmethod/qwininputcontext_win.cpp
patching file mkspecs/win32-g++/qplatformdefs.h
patching file mkspecs/win32-g++/qmake.conf
patch unexpectedly ends in middle of line
patch: **** malformed patch at line 120:

qt-win-opensource-src-4.3.1 $


Under section 8, one might need to add another parameter to ls, namely --color=none.

Jaak Ristioja said...

It may also be necessary to add QT_ARCH_WINDOWS to the mkspec file DEFINES.

Divided Mind said...

Jaak,

thank you for your feedback. I haven't found it necessary to define QT_ARCH_WINDOWS—I think QMake automatically takes care of that.

About the error: it seems that pastebin truncates the final newline of the patch. Just edit it after downloading and make sure it ends with a blank line.

About the ls parameters: on my system ls automatically turns color off when it finds that it's writing to a pipe (instead of a term). If yours does not, it's seriously broken, but go ahead and add this parameter if you find it necessary.

Anonymous said...

When you invoke ./configure, I think the -prefix-install option takes no argument.
Also, I have to specify my machine's endianess through the -little-endian switch.
After that, I run make, but (after many successful commands) I receive this error:

i386-mingw32-g++ -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc -Wl,-s -shared -mthreads -Wl -Wl,--out-implib,/home/manuel/qt-win-opensource-src-4.3.4/lib/libQtGui4.a -o ../../lib/QtGui4.dll object_script.QtGui.Release -L"/home/manuel/qt-win-opensource-src-4.3.4/lib" -L"/home/manuel/qt-win-opensource-src-4.3.4/lib" -lgdi32 -lcomdlg32 -loleaut32 -limm32 -lwinmm -lwinspool -lws2_32 -lole32 -luuid -luser32 -ladvapi32 .obj/release-shared/QtGui_resource_res.o -lmsimg32 -lshell32 -lQtCore4
Creating library file: /home/manuel/qt-win-opensource-src-4.3.4/lib/libQtGui4.a
./.obj/release-shared/qpaintengine_raster.o:qpaintengine_raster.cpp:(.text+0x17c9c): undefined reference to `__imp___ZN6QRectFC1ERK5QRect'
./.obj/release-shared/qpaintengine_raster.o:qpaintengine_raster.cpp:(.text+0x18540): undefined reference to `__imp___ZN6QRectFC1ERK5QRect'
./.obj/release-shared/qpaintengine_raster.o:qpaintengine_raster.cpp:(.text+0x18695): undefined reference to `__imp___ZN6QRectFC1ERK5QRect'
./.obj/release-shared/qstylesheetstyle.o:qstylesheetstyle.cpp:(.text+0x3999): undefined reference to `__imp___ZN6QRectFC1ERK5QRect'
./.obj/release-shared/qstylesheetstyle.o:qstylesheetstyle.cpp:(.text+0x4b3a): undefined reference to `__imp___ZN6QRectFC1ERK5QRect'
./.obj/release-shared/qstylesheetstyle.o:qstylesheetstyle.cpp:(.text+0x4b9b): more undefined references to `__imp___ZN6QRectFC1ERK5QRect' follow
collect2: ld returned 1 exit status
make[2]: *** [../../lib/QtGui4.dll] Error 1
make[2]: Leaving directory `/home/manuel/qt-win-opensource-src-4.3.4/src/gui'
make[1]: *** [release] Error 2
make[1]: Leaving directory `/home/manuel/qt-win-opensource-src-4.3.4/src/gui'
make: *** [sub-gui-make_default-ordered] Error 2

which seems to be due to a symbol declared but never defined.

Anonymous said...

The error should refer to
QRectF::QRectF(QRect const&)
and the export mechanism.
Since this function is defined inline,
I tried to cut the definition and put it in qrect.cpp.
Now the compilation succeed, but a similar error reappear whenever I try to compile the demo. Maybe I miss a flag to properly mangle the names of symbols.

Eric said...

Have you had any luck building Qt 4.4.1? I get the following error when following your instructions. Thank you!

0>make
cd src/winmain/ && make -f Makefile
make[1]: Entering directory `/usr/local/Trolltech/Qt-4.4.1-win/src/winmain'
make -f Makefile.Release
make[2]: Entering directory `/usr/local/Trolltech/Qt-4.4.1-win/src/winmain'
/usr/bin/i586-mingw32msvc-g++ -c -isystem /usr/lib/gcc/i586-mingw32msvc/3.4.5/include -O2 -Wall -fexceptions -mthreads -fno-rtti -DQT_THREAD_SUPPORT -DUNICODE -DQT_TABLET -DQT_SESSIONMANAGER -DQT_NO_DIRECT3D -DQT_NO_CODECS -DQT_NEEDS_QMAIN -DQT_NO_CAST_TO_ASCII -DQT_ASCII_CAST_WARNINGS -DQT_MOC_COMPAT -D_USE_MATH_DEFINES -DQT_NO_DEBUG -I"../../include" -I"tmp" -I"../../include/QtCore" -I"/usr/local/Trolltech/Qt-4.4.1-win/include/qtmain" -I".rcc/release-shared" -I"tmp" -I"/usr/local/Trolltech/Qt-4.4.1-win/include/ActiveQt" -I".moc/release-shared" -I".uic/release-shared" -I"/usr/lib/gcc/i586-mingw32msvc/3.4.5/include" -I"/usr/i586-mingw32msvc/include" -I"../../mkspecs/win32-g++" -o .obj/release-shared/qtmain_win.o qtmain_win.cpp
/usr/bin/i586-mingw32msvc-ar -ru ../../lib/libqtmain.a .obj/release-shared/qtmain_win.o
/usr/bin/i586-mingw32msvc-ar: creating ../../lib/libqtmain.a
make[2]: Leaving directory `/usr/local/Trolltech/Qt-4.4.1-win/src/winmain'
make[1]: Leaving directory `/usr/local/Trolltech/Qt-4.4.1-win/src/winmain'
cd src/tools/moc/ && make -f Makefile
make[1]: Entering directory `/usr/local/Trolltech/Qt-4.4.1-win/src/tools/moc'
make[1]: Nothing to be done for `first'.
make[1]: Leaving directory `/usr/local/Trolltech/Qt-4.4.1-win/src/tools/moc'
cd src/tools/rcc/ && make -f Makefile
make[1]: Entering directory `/usr/local/Trolltech/Qt-4.4.1-win/src/tools/rcc'
make[1]: Nothing to be done for `first'.
make[1]: Leaving directory `/usr/local/Trolltech/Qt-4.4.1-win/src/tools/rcc'
cd src/tools/uic/ && make -f Makefile
make[1]: Entering directory `/usr/local/Trolltech/Qt-4.4.1-win/src/tools/uic'
make[1]: Nothing to be done for `first'.
make[1]: Leaving directory `/usr/local/Trolltech/Qt-4.4.1-win/src/tools/uic'
cd src/corelib/ && make -f Makefile
make[1]: Entering directory `/usr/local/Trolltech/Qt-4.4.1-win/src/corelib'
make -f Makefile.Release
make[2]: Entering directory `/usr/local/Trolltech/Qt-4.4.1-win/src/corelib'
/bin/sh: -c: line 1: syntax error: unexpected end of file
make[2]: *** [.pch/release-shared/qt_pch.h.gch/c++] Error 2
make[2]: Leaving directory `/usr/local/Trolltech/Qt-4.4.1-win/src/corelib'
make[1]: *** [release] Error 2
make[1]: Leaving directory `/usr/local/Trolltech/Qt-4.4.1-win/src/corelib'
make: *** [sub-corelib-make_default-ordered] Error 2

Eric said...

I changed:
QMAKE_CHK_DIR_EXISTS = if not exist
To:
QMAKE_CHK_DIR_EXISTS = test -d

This fixed the compile ... how did this work for everyone else?

Thank you!
Eric

Divided Mind said...

eric, frankly I haven't used qt/win for quite some time now, but I'm glad my howto still helps (although I'd be even more happy if the Trolls could make it obsolete by changing the build system play to nice with xmingw).

But I took a look at the current qt-copy version and it does seem to have QMAKE_CHK_DIR_EXISTS already fixed (as long as the MINGW_IN_SHELL branch is followed).

CMoH said...

Thank you for this post. It is still very useful, and it was my main guide to cross-compiling Qt 4.5.3 on my Linux box.

To add some notes from my experience:

1. I had to remove the dir in mkspecs/features for the ln -s to work.
2. I enabled some extra-modules that I need for my applications (opengl, webkit, xmlpatterns) and I had to do some comments in the linux-based configure script for this to work.
3. The patch did not work, so I had to read it and apply the changes by hand to the files there.
4. To compile WebKit I had to change an #include to #include in some file (can't remember which, some plugin). Also, had to disable pch (-no-pch)
5. It appears that when cross-compiling 32-bit code on an amd64 arch gcc gives some strange assembler errors. After browsing PLATFORM and XPLATFORM in the configure script I found that I had to add the -platform flag as well.

Hope I did not miss anything. If I did, at least you know it is doable :)

My final configure line was:

./configure -prefix /opt/xmingw -prefix-install -xplatform win32-g++ -no-largefile -exceptions -no-accessibility -no-qt3support -make libs -make translations -no-reduce-exports -opensource -little-endian -host-little-endian -webkit -xmlpatterns -opengl -stl -platform linux-g++-32 -no-pch

(-prefix-install still appears not to have any effect)

+Adikkto said...

Hello, this is the only post i have found about cross compiling qt on linux but i can't download your patch, it seems the link is dead. I hope you can fix it. I want to cross compile qt 4.7.1, maybe you can assist me.
Bye

Divided Mind said...

+Adikkto, I've lost interest in cross compiling since Nokia provides premade LGPL binaries for Windows now. Anyway, I've reuploaded the patch.

CoolPics said...

Anyone please guide me how to create simple application for touch pad embedded device micro-arm 2440 on windows, please i am beginner in this, give me detailed step by step procedure.... Please reply :)