I’ve been following the Necessitas project (formerly “Android Lighthouse”) for a while now. It’s a port of the Qt GUI framework to Android. Last year I managed to get it to build on Windows by following the instructions at android-lighthouse google code ticket 11 and this very helpful post by Damien Buhl (in French, Chrome will translate it for you). Those links are still super helpful, but some things have changed since then — Necessitas has undergone significant structural change, and Google have released a new Android NDK r5b.
This post documents the steps I took to get the Necessitas Qt framework to compile with Cygwin and the official Android NDK r5b (the latest version as of this writing). So far I’ve only got the libraries to compile. I havn’t built Qt Creator (the GUI IDE that Necessitas now offers) and nothing is running on my device yet — that’s a matter for more research. I thought I’d post the info now because I seem to have got it to build, and well, there’s more than enough info here for a blog post.
*Disclaimer:* If you follow these instructions you will have built Qt libraries for Android with Cygwin, nothing more. I don’t tell you how to use them or how to load them onto the device, nor do I tell you how to get Necessitas Qt Creator running on Windows.
Cygwin vs. mingw
Windows gcc development can be done with the Cygwin POSIX emulation layer, or with mingw — a light weight minimalist gcc distribution. So far Android NDK development on Windows requires the Cygwin environment. However Google is in the process of dropping support/dependency on Cygwin from the Windows Android NDK. This is good and bad news. Good because NDK builds with Cygwin are slow, and installing Cygwin just to build NDK code is a lot of overhead. Bad news is that the native Qt build and configuration system doesn’t support cross-compiling Qt on Windows without Cygwin, and using the new NDK in combination with Cygwin-based Qt builds is non-trivial.
I tried to build Qt Necessitas with both mingw and Cygwin. I made progress with both but in the end I succeeded with Cygwin first, so that’s what this post is about. In the long term I think cross-compiling Qt using mingw on Windows is most likely a better solution but it will require substantial work on Qt’s configuration system before it is possible. I just learnt that mingw.android is working on it.
The official Necessitas instructions for compiling the Qt framework describe how to build Qt frameworks for Android on Linux. There are a few extra steps to make this process work on Windows under Cygwin.
You need to have Cygwin installed, with the development tools (binutils, gcc-core, gcc-g++, make, patchutils) and Cygwin git. It’s all easy to install using cygwin’s package installer. You may need a few other Cygwin packages. You’ll soon know about it if you try to run a command that isn’t installed.
Also have the Windows Android NDK r5b downloaded and unzipped.
Clone the Qt source code from the android-lighthouse repository using git (it’s about 400MB and might take a while):
A few points here:
- Make sure you’re typing everything here in a Cygwin shell and using Cygwin git. You want the symlinks in the repo to come out correctly as Cygwin symlinks.
- Early on I had a problem using msys git where things wouldn’t build because git had converted everything to Windows-style CRLF line endings. So you might want to make sure your git config says core.autocrlf=false
- Once git is finished I recommend making a local copy of the android-lighthouse directory so you have a clean version you can duplicate if you want to start again. I found this easier than cleaning the tree if the configuration breaks badly.
Another thing to note: I have the NDK and android-lighthouse directories on the same drive. I’m not sure, but this might help in cases where commands ignore drive letters. Best to keep them on the same drive to be safe.
NDK compiler include paths
Since the NDK r5b gcc compiler no longer depends on Cygwin, it doesn’t understand Cygwin’s POSIX style paths that map Windows paths like C: /blah to /cygdrive/c/blah. When you configure and build Qt using Cygwin, qmake and all the Qt Makefiles will use Cygwin POSIX style paths. This works out OK for relative paths: the NDK gcc can deal with forward-slash path separators, but it can’t follow the Cygwin style /cygdrive/blah absolute paths used for some include and library directory arguments. The net result is that gcc will report it can’t find standard header files like <string.h> or <new>. If you invoke gcc with the -v option you get the real story: it can’t interpret the Cygwin paths and is ignoring them.
I tried a few of different fixes for this issue:
Attempt #1: Pass include paths to qmake as Windows paths, executable tool paths as Cygwin paths. This doesn’t work. Not only does qmake pass include paths to gcc, it also uses them internally to compute dependency information that gets embedded in the generated Makefiles. Qmake and make need to be able to understand all paths you use. Therefore you need to use Cygwin paths at this level.
Attempt #2: Defining QMAKE_RUN_CC and friends in mkspecs/android-g++/qmake.conf allows you to override how qmake invokes gcc (grep other qmake.conf files for examples). I was able to wrap $INCPATH with some sed foo that converts Cygwin paths to Windows paths which gcc would understand. This almost worked. Unfortunately qmake hardcodes (in qmake/generators/unixmake2.cpp) the $INCPATH text for generating the Makefile rule for precompiled headers, so it’s not possible to re-write the paths in all invocations of gcc using qmake.conf. I could have just disabled precompiled headers (-no-pch flag to ./configure) but that’s not a real solution.
Solution: having given up on a purely qmake.conf approach I decided to just wrap gcc and g++ with shell scripts that re-write include path (-I) and library path (-L) flags that use Cygwin paths. These scripts go in android-ndk-r5b\toolchains\arm-linux-androideabi-4.4.3\prebuilt\wi ndows\bin along side the NDK gcc and g++ binaries:
arm-linux-androideabi-cygwin-gcc.sh:#!/bin/sh b=`basename $0` d=`dirname $0` p=`echo $@ | sed -e "s,-I/cygdrive/\(.\)/,-I\1:/,g" | sed -e "s,-L/cygdrive/\(.\)/,-L\1:/,g"` $d/arm-linux-androideabi-gcc.exe $p
arm-linux-androideabi-cygwin-g++.sh:#!/bin/sh b=`basename $0` d=`dirname $0` p=`echo $@ | sed -e "s,-I/cygdrive/\(.\)/,-I\1:/,g" | sed -e "s,-L/cygdrive/\(.\)/,-L\1:/,g"` $d/arm-linux-androideabi-g++.exe $p
Then the definitions of QMAKE_CC and QMAKE_CXX in android-lighthouse/mkspecs/android-g++/qmake.conf need to be modified to invoke the wrapper scripts instead of the NDK gcc and g++ executables:
QMAKE_CC = $$NDK_TOOLCHAIN_PATH/bin/$$NDK_TOOLCHAIN_PREFIX-cygwin-gcc.sh ... QMAKE_CXX = $$NDK_TOOLCHAIN_PATH/bin/$$NDK_TOOLCHAIN_PREFIX-cygwin-g++.sh
Symbolic links are not well supported on Windows (although Vista and Windows 7 do add some support). Cygwin provides emulated symbolic links, but of course the new r5 NDK gcc can’t understand them. This causes two separate problems, solved thusly:
1. The current android-lighthouse tree uses symlinks in a number of places to alias .c and .h files to different locations in the tree. I’ve filed a ticket about this not working on Windows, but until it’s fixed you can run the following command (from a Cygwin prompt) to replace the symlinks with normal C files that #include the linked file:
find -type l -regex '.*/.*\.\(c\|cpp\|h\)$'-printf "rm %p; echo '#include \"%l\"' > %p\n" | sh(all of the above on one line)
2. The Qt build process creates symlinks to redirect general shared library .so files to the version numbered ones (eg. libQtCore.so is a symlink to libQtCore.so.4.8.0). Since the Qt build process crosslinks Qt libraries with the general (usually symlink) versions this breaks if gcc can’t understand the symlinks. Qmake would usually use the ln -s command to create a symbolic link. The easy (but space hungry) fix is to tell Qt to just copy the libraries instead of linking them by adding the following to android-lighthouse/mkspecs/android-g++/qmake.conf:
QMAKE_LN_SHLIB = cp
Incidentally, that’s the behaviour of ln -s when you invoke it in msys/mingw.
There are a couple of patches that have been floating around for a while that are required to make the Necessitas framework build with Cygwin. I think I found them on the google code ticket 11 page. Both are needed so that the Qt bootstrapping process can compile tools such as qmake, moc and rcc using the Cygwin toolchain. The first patch adds library prefixes and extensions to the Cygwin g++ qmake.conf, the second resolves a Unicode issue in src/corelib/global/qglobal.cpp by replacing OSVERSIONINFOW with _OSVERSIONINFOA.
diff --git a/mkspecs/cygwin-g++/qmake.conf b/mkspecs/cygwin-g++/qmake.conf index ddfceb0..7b54298 100644 --- a/mkspecs/cygwin-g++/qmake.conf +++ b/mkspecs/cygwin-g++/qmake.conf @@ -25,6 +25,9 @@ QMAKE_CFLAGS_DEBUG = -g QMAKE_CFLAGS_SHLIB = QMAKE_CFLAGS_YACC = -Wno-unused -Wno-parentheses QMAKE_CFLAGS_THREAD = -D_REENTRANT +QMAKE_PREFIX_SHLIB = lib +QMAKE_PREFIX_STATICLIB = lib +QMAKE_EXTENSION_STATICLIB = a QMAKE_CXX = g++ QMAKE_CXXFLAGS = $$QMAKE_CFLAGS diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index 6105682..fad4c40 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -1742,7 +1742,8 @@ QSysInfo::WinVersion QSysInfo::windowsVersion() if (winver) return winver; winver = QSysInfo::WV_NT; - OSVERSIONINFOW osver; + _OSVERSIONINFOA osver; osver.dwOSVersionInfoSize = sizeof(osver); GetVersionEx(&osver); #ifdef Q_OS_WINCE
androidconfigbuild.sh bug fix
As of this writing there’s a bug in androidconfigbuild.sh’s use of the getopt built-in that means that the script ignores host and platform parameters passed on the command line (we need to be able to specify the host as windows). It should be fixed by the time you read this, but just to be sure, make sure the getopt line looks like this:
while getopts "Hr:h:p:v:a:q:c:i:d:" arg; do
The version of androidconfigbuild.sh I checked out of git passed a getopts parameter like this: “help:r:h:p:v:a:q:c:i:d:” That’s a problem because getopts doesn’t support multi-character flags which “help” is, and in any case the -help flag doesn’t accept a parameter so it shouldn’t have a trailing “:”.
Disable webkit build
During the build I encountered a problem with Windows’ 32k limit on command-line length. This meant that when the Qt build got around to linking the webkit library it failed with an “argument list too long” error. The command line arguments were about 60k. I don’t have a fix for this other than to disable building webkit. Do this by editing androidconfigbuild.sh, change the -webkit parameter to configure to -no-webkit.
Now we’re ready
If you’ve followed along you’re ready to actually launch the Qt build. From a Cygwin prompt type something like the following. You’ll have to substitute the Cygwin style path of your Android NDK. Mine’s in the root of G: drive:
./androidconfigbuild.sh -r /cygdrive/g/android-ndk-r5b -h windows-p arm-linux-androideabi -v 4.4.3 -q 1(all of the above on one line)
This should proceed through the entire build process. It will print error messages at times, usually these are just configuration tests for whether things are present and functioning. Unless the process completely halts and returns to the command prompt you can trust that it’s doing its thing.
The configure and make process will complete but the final two steps in androidconfigbuild.sh will fail (qpatch and make install). I’m not sure whether these steps are important on Windows unless you want to relocate the libraries once you’ve built them. I’ll update the post if I find an answer, for now I think it should work without them.
Cygwin Win32 error 487 contingency
This may not happen to you, but after my build was about half way through it failed with an error like “could not load C:\WINDOWS\system32\winmm.dll, Win32 error 487.” A bit of digging suggests that this is a known issue with the current Cygwin build. I fixed it by copying the latest cygwin1.dll snapshot over the version in my Cygwin install path at C:\cygwin\bin\cygwin1.dll. After that I restarted the build and the error went away (you can pass -q 0 instead of -q 1 to prevent androidconfigbuild.sh from running configure from the start). If you’re brave you may want to update your cygwin1.dll before you start but I can’t guarantee that won’t introduce other problems.
Right now that’s what you need to do to build Qt/Necessitas with Cygwin. You won’t have Qt Creator and at this stage I can’t tell you how to install Qt projects on an Android device without it. You used to be able to just push everything with adb but things seem to have changed a bit since I last followed Damien Buhl’s instructions for manually pushing libs to your device using adb. Once I work that out I’ll post an update here. Please subscribe to the blog or follow my tweets if you want to hear about the next installment.
Thanks to BogDan for his great work on Necessitas, and to all the helpful developers on android-qt and android-ndk lists. Thanks also to Damien Buhl and the posters on googlecode. If I hadn’t succeeded in getting a previous version of android-lighthouse building with their instructions I probably wouldn’t have known where to start this time around. Thanks to David Turner on android-ndk for answering my questions about current Windows NDK status and to mingw.anrdroid who should be bringing us some Cygwin-free Necessitas goodness soon.