Nice article, thank you for sharing.
Have you ever written a library on top of Qt? It is actually quite ease. Consider we have a very simple library called
my-qt-lib, with the following structure:
. ├── myqtlib.cpp ├── my-qt-lib_global.h ├── myqtlib.h └── my-qt-lib.pro
This is a very simple skeleton which Qt Creator usually creates for your in case you ask it to create a shared library project. Let’s have a closer look at the file
my-qt-lib.pro, where we need to apply some changes:
QT -= gui TARGET = my-qt-lib TEMPLATE = lib DEFINES += MYQTLIB_LIBRARY DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ myqtlib.cpp HEADERS += \ myqtlib.h \ my-qt-lib_global.h # Install the library into the Qt # installation's libraries directory: target.path = $$[QT_INSTALL_LIBS] INSTALLS += target # Install the headers into the Qt # installation's include directory: headers.files = myqtlib.h my-qt-lib_global.h headers.path = $$[QT_INSTALL_HEADERS] INSTALLS += headers
That’s also quite simple and mostly what Qt Creator is able to create for you automatically. The interesting thing are the few lines at the end. We set up custom installation paths for our library, pointing it to libraries installation location of the current Qt installation (by using
$$[QT_INSTALL_LIBS]). In addition, we create a custom install target
headers, which we use to copy the header files into the Qt installation (concrete, into the include directory, which we get by using
With this setup, we can do the following to install our library into a Qt installation:
mkdir build-linux-x86_64 cd build-linux-x86_64 $HOME/Qt/5.12.2/gcc_64/bin/qmake .. make make install
The last line will actually copy the library plus header files into the Qt installation. To better understand, what gets copied where, let’s tweak it and see which files would get copied:
make install INSTALL_ROOT=$PWD/tmp
This will basically do a “fake” installation into the sub-directory
tmp/ inside the current directory. Let’s check the content of this directory:
tmp └── home └── martin └── Qt └── 5.12.2 └── gcc_64 ├── include │ ├── my-qt-lib_global.h │ └── myqtlib.h └── lib ├── libmy-qt-lib.so -> libmy-qt-lib.so.1.0.0 ├── libmy-qt-lib.so.1 -> libmy-qt-lib.so.1.0.0 ├── libmy-qt-lib.so.1.0 -> libmy-qt-lib.so.1.0.0 └── libmy-qt-lib.so.1.0.0
Cool, exactly what we would have expected: The process would have copied the library (including some symlinks, which are typical for a Linux installation of a library) as well as the header files into the Qt installation. Great.
What is this useful for? Well, if you have an addon library for Qt, you can easily install it into your Qt installation and every other library or app building against this Qt will be able to use your library. Fancy, isn’t it?
There is just one little issue. Let’s assume we also want to use our library on Android. Consequentially, we need to install it into our Android Qt installation. Let’s go:
# Make the path to the Android NDK known: export ANDROID_NDK_ROOT=$HOME/Android/android-ndk-r19c mkdir build-android-armv7 cd build-android-armv7 $HOME/Qt/5.12.2/android_armv7/bin/qmake .. make make install INSTALL_ROOT=$PWD/tmp
The last line will again install the library into a temporary directory, so we can check the installation:
tmp/ ├── home │ └── martin │ └── Qt │ └── 5.12.2 │ └── android_armv7 │ └── include │ ├── my-qt-lib_global.h │ └── myqtlib.h └── libs └── armeabi-v7a └── libmy-qt-lib.so
Uff, and here, something is wrong. The headers are correctly installed. However, the library would get installed into
/libs/armeabi-v7a. What went wrong?
The answer is simple:
qmake has some built in magic when it comes to Android. You can read about this in detail in the official docs. The point is: This magic prevents us from installing the library where we want it to land.
As always, the solution to this issue is not as well documented as it could be, but sooner or later you land in Qt’s bug tracker and there indeed you find a solution. We just need to remove the
android_install configuration option:
QT -= gui TARGET = my-qt-lib TEMPLATE = lib DEFINES += MYQTLIB_LIBRARY DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ myqtlib.cpp HEADERS += \ myqtlib.h \ my-qt-lib_global.h target.path = $$[QT_INSTALL_LIBS] INSTALLS += target headers.files = myqtlib.h my-qt-lib_global.h headers.path = $$[QT_INSTALL_HEADERS] INSTALLS += headers # Disable qmake's magic for installing libraries # on android: CONFIG -= android_install
If we repeat the above steps to build and install the library, we now get the expected results:
tmp └── home └── martin └── Qt └── 5.12.2 └── android_armv7 ├── include │ ├── my-qt-lib_global.h │ └── myqtlib.h └── lib └── libmy-qt-lib.so
Very good! However, we now have a little problem: If someone uses our library as a sub-module inside another project, he will not be able to use qmake’s magic in case he targets Android. So in case we want to allow both (installation of the library into the Qt installation when building standalone as well as deployment to the standard location in case of it is built as part of another project), we can just keep the
*.pro file as it is and instead modify the call to
qmake on the command line:
# Disable the special Android library handling on command line: $HOME/Qt/5.12.2/android_armv7/bin/qmake CONFIG-=android_install .. # And now proceed as usual: make make install
Nice article, thank you for sharing.