I just started working on a simple cross-platform desktop application. I’m developing from Linux and hoping to build binaries for Linux, Windows and OS X.
This quick blog post are some notes I took about:
* Developing & building a Qt app for Windows, on Linux with wine.
* Building static Qt libraries for Windows, on Linux with wine, using mingw32.
* Building a standalone static linked Qt executable for Windows, using mingw32.
This information is mostly out there somewhere on the web, but spread among lots of sources (many out of date.) I thought it would help to rewrite my experiences start-to-finish here.
Building Qt apps under wine
This is almost insanely easy. In my case I downloaded Qt Creator 2.21 for Windows & Qt 4.7.3 for mingw32, both directly from nokia. Ran the installers with
The Qt installer needs to be pointed at a mingw32 installation. One comes bundled with Qt Creator, so if you’ve used the default install path it will be C:\Qt\qtcreator-2.2.1\mingw\bin\
Kudos to both the Wine team and the Qt developers.
Developing with Qt Creator
If you’re using Qt Creator as an IDE, and happy with dynamic linking (ie distributing DLLs with your application), then you’re pretty much finished. I found I was able to keep the same project open in both Linux & Wine versions of Creator simultaneously (one would prompt to reload when I’d made changes in the other.)
Mostly everything just worked!
EDIT: OK, not quite. Autocomplete seems to lock up Creator in wine for ~20 seconds each time, so it’s not so good for on-the-fly coding.
Only one oddity, on the Wine side I seem to need to “Rebuild” more often that I probably should. Don’t know if that’s a Windows thing, a Qt thing, or a Wine thing.
Command Line Building
The Qt installer for Windows installs a “Qt Command Prompt” program shortcut for Qt command line stuff. This doesn’t work under Wine. All you need to do, however, is to set some environment variables.
Wine inherits environment variables from its parent shell, so in theory you can just set variables (except for PATH) in your Linux shell and it’ll all work just fine. However, you then run the risk of confusing your Linux build environment.
My preferred approach is to use wine’s regedit to set the variables in the registry:
cat > /tmp/qttemp.reg << EOF
Now commands like "wine qmake myproject.pro" and "wine mingw32-make" should work.
You may want to static link your Windows Qt app for deployment. This, essentially, means you no longer have to distribute ~12Mb of release DLLs (QtCore4.dll, QtGui4.dll, libgcc) with your application (~6Mb if zipped.)
Instead, if static linked, a 600kb application will grow out to around 11Mb itself (4.5Mb zipped), but will run standalone. However, you'd be able to use an Executable Packer like UPX if you wanted to, to get it down under 4Mb.
Think hard if this is all really worth the effort compared to shipping DLLs. The Qt documentation has a good discussion of the pros & cons of static linking, and explains the basic steps to rebuild Qt on Windows to allow static linking.
Note that if static linking the open source edition of Qt, you'll have to make sure you can comply with the terms of the license - either LGPL 2 or GPL 3.
Here are the steps I took under wine, they are a variation of the steps outlined for Windows in the Qt docs.
First I needed to set the command line build environment variables, as described above.
I copied my existing Qt Windows tree to a new directory, so I could have side-by-side dynamic & static options (AFAIK this is the only way to have them side-by-side.)
cp -al 4.7.3 4.7.3-static
After this, run 'wine regedit' and change the paths to Qt which are set under HKEY_CURRENT_USER\Environment.
If using Qt Creator, you may also need to update the Qt installation listed under Tools -> Options -> Qt4 (if it isn't autodetected from PATH.)
Newer mingw32 version
When built with older versions of mingw32, applications would depend on mingw32.dll. This is a ~10k DLL that contains some functions for thread exception support. It has to be a DLL, cannot be built statically. To deploy your application you would have had these choices:
- Bundle mingw32.dll (a public domain DLL so no licensing issues.)
- Don't use exceptions, and configure/compile everything with -no-exceptions.
- Don't use threads, ie no -mthreads.
However, in the most recent version of mingw32 (gcc 4.5.2) this restriction has been lifted. QT Creator is bundled with the older mingw32, using gcc 4.4.0. So, if you want a fourth option, you can:
- Download the latest mingw32 installer and run it under wine.
- Select "C++ compiler" and "MSYS Basic System" when prompted by the installer.
- Run 'wine regedit' and edit PATH under HKEY_CURRENT_USER\Environment to point to the new mingw\bin directory.
- If using QT Creator, go to Tools->Options->Toolchains and add a new MinGW entry for that compiler. Then go to Projects -> Build Settings in your project and switch to the new compiler entry.
(NB: for reasons I don't actually comprehend, you may still need mingw32.dll for debug builds. Although this may just be a sign I need to rebuilt my entire Qt oncem ore.)
Configuring Qt for static libraries
Configure as per the documentation. You'll be prompted for license choice, etc.
wine configure.exe -static
Make in parallel
Building Qt with mingw32 is excruciatingly slow. To make things faster, you can force make to use multiple parallel jobs:
MAKE="mingw32-make -j4" wine mingw32-make sub-src
Replace "4" with the number of parallel compilations you'd like to see.
The full build with -j4 on my quad core i5 2.7Ghz machine takes about 75 minutes, including restarting after the errors (see below.)
Cope with errors
Unfortunately, building Qt 4.7.3 in parallel will lead to false errors towards the end - some codec-related plugins do not have their dependencies set quite right. The easiest thing to do is to just restart from here without -j set, the build only has about 10 minutes to run at this point anyhow.
Also, YMMV but one time I had Qt fail to link libbootstrap - claiming missing functions from QLocale. I don't know what this was caused by, seemingly nothing to do with parallel make, but I deleted tools/bootstrap/tmp/obj/release_static/qlocale.o , ran mingw32-make again, and it was fine.
Add static options to project
Finally, once you're done building Qt, you still need to add
QMAKE_LFLAGS += -static -static-libgcc
to your .pro file(s), in order to statically link in libgcc & libstdc++.
... and Static linking is set up for Windows! Hope it was worth it. ;)
Disadvantages To using Qt with Mingw32 under Wine
Compilation is slow
Apparently this is mostly mingw32's slowness, although building under wine can't be helping. Regardless, it is many times slower than native building on Linux.
Parallel building makes a massive improvement though, with modern multicore CPUs. If you're running with Qt Creator, you can force your whole project to build in parallel by going to Projects -> Build Settings -> expanding the 'Make:' build step and setting Make arguments to -j3 MAKE="mingw32-make -j3" (where 3 is your number of parallel steps.) YMMV on that, though. You may need to better specify dependencies, as described here.
Even with this setup, you still need a "real" Windows machine (or VM) to do final testing. Although this way you can avoid contact with as many Windows subsystems as theoretically possible, and just copy across your built app to test. :)
It looks like it may be possible to install the Microsoft Platform SDK tools under wine and use Qt with the Visual Studio platform, but I haven't tried.
Or, apparently it is possible to install mingw32 as a cross-compilation toolchain. This way the the compiler runs directly on Linux instead of under Wine. I haven't tried this, not sure how it would compare. You certainly wouldn't be able to use that toolchain with QT Creator, I don't think.
Finally, you could use a VM or a real Windows box. I prefer working with wine, though (ideally I'll touch the bare minimum amount of Windows internals that are necessary. ;))
Cross-compiling Qt apps for OS X (Ref A, ref B.) Just kidding, not that masochistic. I think I'll go and find a real Mac to borrow. ;)