Qt 6 Build System

CMake is the build system for Qt 6

The tool that's used to build Qt 6 is CMake.

CMake is

  • widely adapted and available.
    It's even shipped with Visual Studio these days.
  • supported by a thriving community.
    Many packages for third-party libraries are available.
  • stable and mature.
    Next year, it will get permission to consume alcohol beverages in the US.

CMake 3.18.4 was released recently. Please use that one for building Qt.

Use configure or plain CMake

Our beloved configure script still exists. It accepts most of the options you are used to from Qt 5.

At the moment the qmake build system is still in place. You can choose between building Qt with qmake or CMake. Pass -cmake to configure to use the CMake-based build.

Update: The CMake build system is now the default one.
There is no need to pass -cmake anymore to select the CMake build.
Pass -qmake to enable the qmake build, but be prepared that it is going to vanish anytime soon.

For the final release, expect that the qmake build system will vanish together with the need to pass -cmake.

For example, the following call configures Qt to be installed to ~/Qt/6.0.0 and turns off support for the QLCDNumber widget and skips the qtsvg repository:

configure -cmake -prefix /opt/Qt/6.0.0 -no-feature-lcdnumber -skip qtsvg

The configure script translates those arguments to the following CMake call:

cmake -GNinja -DCMAKE_INSTALL_PREFIX=/opt/Qt/6.0.0 -DFEATURE_lcdnumber=OFF -DBUILD_qtsvg=OFF /path/to/qt/sources

This is quite a mouthful, and illustrates the usefulness of our configure script.

With configure, we can pass extra arguments to CMake after the -- argument. The configure call above is equivalent to this:

configure -cmake -prefix ~/Qt/6.0.0 -no-feature-lcdnumber -- -DBUILD_qttools=OFF

After configuring, use cmake --build . or ninja to build the project.

We recommend to use CMake's Ninja generator. This is also configure's default.

You can specify the CMake generator by passing -cmake-generator <name> to use another generator and -cmake-use-default-generator to use CMake's default generator, i.e. no -G <generator> argument is passed.

Use cmake --install . to install Qt.

Cross-compiling Qt

To cross-compile a project with CMake, one must specify a toolchain file. This CMake-language file sets the right values for the platform name, used compiler/linker and a whole bunch of other toolchain-specific things.

The toolchain file concept is similar to qmake's mkspecs.

Read more about toolchain files in CMake's cross-compiling documentation.

Let's examine how to do an Android build of Qt on Linux.

configure -cmake -prefix /opt/Qt/6.0.0-android \
    -qt-host-path /opt/Qt/6.0.0 \
    -xplatform android-clang \
    -android-ndk ~/Android/android-sdk-tools/ndk-bundle \
    -android-sdk ~/Android/android-sdk-tools \
    -android-abis armeabi-v7a

The most notable difference is the -qt-host-path option. To cross-build Qt, you need to point to an already existing native build of your host platform. This is called the host Qt.

All the native host tools like moc, rcc and uic are run from there.

In Qt5, we built the host tools anew for every single cross-build. Re-using the host tools from an existing host Qt installation saves us from building the host tools for every cross-build over and over again and greatly simplifies Qt's build system.

The cross-built Qt does not build the host tools for the target platform, unless you set the CMake variable QT_BUILD_TOOLS_WHEN_CROSS_COMPILING to ON. Doing so is useful in embedded Linux environments.

The -xplatform android-clang argument merely influences the companion files that are generated to provide qmake-support for this cross-build.

The -android-ndk argument points to the NDK and selects the CMake toolchain file that's located in there.

The -android-sdk argument points to the SDK.

The -android-abis argument selects the ABI we're going to build. Note that multi-ABI builds are not yet implemented.

And to put everything together, the configure call above is translated into the following CMake call:

cmake -DQT_HOST_PATH=/opt/Qt/6.0.0 \
    -DQT_QMAKE_TARGET_MKSPEC=android-clang \
    -DANDROID_SDK_ROOT=/home/jobor/Android/android-sdk-tools \
    -DCMAKE_TOOLCHAIN_FILE=/home/jobor/Android/android-sdk-tools/ndk-bundle/build/cmake/android.toolchain.cmake \
    -DANDROID_ABI=armeabi-v7a \
    -DCMAKE_INSTALL_PREFIX=/opt/Qt/6.0.0-android \
    -G Ninja \
    /path/to/qt/source

Again, build with cmake --build . or just ninja.

Use cmake --install . to install Qt.

Building Qt-based Projects

Once you have installed Qt, you can use CMake or qmake to build your projects.

Business as usual with qmake:

mkdir ~/my-project-build
cd ~/my-project-build
/opt/Qt/6.0.0/bin/qmake
make

Note: This also works for cross-builds. There, qmake is a wrapper script that calls the qmake executable of the host Qt with a generated qt.conf that sets the right paths and mkspec.

Qt comes with a wrapper script called qt-cmake to offer a similar experience with CMake:

mkdir ~/my-project-build
cd ~/my-project-build
/opt/Qt/6.0.0/bin/qt-cmake
cmake --build .

qt-cmake calls CMake with the CMAKE_TOOLCHAIN_FILE variable set to a Qt-internal toolchain file that sets the right compiler and chainloads the "real" toolchain file in the cross-compiling case.

Note: If you find yourself needing to chainload a different toolchain file, pass -DQT_CHAINLOAD_TOOLCHAIN_FILE=<file-path> to qt-cmake.

qt-cmake does not set the CMake generator. If you always want to use Ninja - what we recommend - set the environment variable CMAKE_GENERATOR to Ninja.

Building Qt Modules

qt-configure-module is the tool of choice when building a Qt module separately against an installed Qt. The qt-configure-module script takes the same arguments a top-level Qt configure call would - restricted to the arguments that apply to the module we're currently configuring.

In the following example we first build and install qtbase, then qtdeclarative with the qml-network feature turned off.

mkdir ~/dev/qt/qtbase-build
cd ~/dev/qt/qtbase-build
../qtbase/configure -prefix /opt/Qt/6.0.0
cmake --build .
cmake --install .

mkdir ~/dev/qt/qtdeclarative-build
cd ~/dev/qt/qtdeclarative-build
/opt/Qt/6.0.0/bin/qt-configure-module ../qtdeclarative -no-qml-network
cmake --build .
cmake --install .

Building stand-alone Tests and Examples

If you're a Qt contributor, hacking on some part of Qt, you probably want to extend and run the autotests that cover that area. But most people always configure Qt with -nomake tests, because configuring and building tests takes some time.

There are two good news:

  1. Not building all tests is the default now! No need to pass -nomake tests anymore. The same goes for -nomake examples.
    If you really want to build all test and examples, pass the -make tests -make examples arguments. We believe that the new default will result in much less typed commandline characters, saving this scarce resource for more interesting invocations.
  2. It's easy to configure and build a single autotest or example.

And this is how you build and run the autotest for QProcess:

mkdir ~/dev/qt/tst_qprocess
cd ~/dev/qt/tst_qprocess
/opt/Qt/6.0.0/bin/qt-cmake-standalone-test ../qtbase/tests/auto/corelib/io/qprocess
cmake --build .
ctest -V

This uses the ctest tool that comes CMake, but you can also directly run the executable, of course.

Building an example works in a similar way:

mkdir ~/dev/qt/wiggly
cd ~/dev/qt/wiggly
/opt/Qt/6.0.0/bin/qt-cmake ../qtbase/examples/widgets/widgets/wiggly
cmake --build .
./wiggly

Porting qmake-based Projects to CMake

If you're looking for a way to easily port your qmake-based project to CMake, I have good and bad news for you.

The Good News

There is no immediate need to port your project to CMake.

QMake continues to work in Qt6.

The Bad News

There is no good porting tool available.

Qt itself was (and to a certain degree still is) ported to CMake using a Python script called pro2cmake.

The script is tailored to the structure of Qt module project files, and while it potentially could be useful to create a starting point for user projects, it is far from something we would recommed.

It might just bail out on encountering some clever qmake construct.
But if you're of the adventurous type, feel free to try it out, and make sure to pass the --is-example argument.

Conclusion

We hope that the switch to CMake is as smooth as possible for you and that Qt and CMake communities will benefit from each other.

Thanks for reading, and do not hesitate to try out the Qt CMake build and provide feedback in the comment section or in our bug tracker!


Blog Topics:

Comments

Commenting for this post has ended.

P
Pau
5 points
55 months ago

How come Qt 6 still uses moc instead of taking verdigris or implementing its own template-based moc replacement? https://github.com/woboq/verdigris

Mykola Krachkovsky
1 point
55 months ago
  1. That "replacement" doesn't support some features, has own syntax and needs code duplicate, so it's not an actual replacement.
  2. Why? moc is just a tool to make life easier.
P
Pau
0 points
55 months ago
  1. Actually, it's the other way around: moc does not support templated QObjects, headers with raw string literals and plenty of other modern C++ features. Its own syntax is needed just to avoid clashing with moc, that need is gone once moc is gone. As for code duplicate, please elaborate. Verdigris has been an actual full moc replacement for years.
  2. Because moc is unneeded, as verdigris proves, and it has some limitations that verdigris lifts. Also, two preprocessors (cpp and moc) make errors sometimes difficult to debug.
Mykola Krachkovsky
2 points
55 months ago

1.1. Status in readme has several features it misses.

1.2. Using insted of public slots: void mySlot(int x);void mySlot(int x); W_SLOT(mySlot)is code duplication, you have to write (and rename in case of refactoring) same slot twice. And this is incompatible with existing code. So this is not a replacement.

2. That code can't replace existing moc, so no, moc is still needed.

K
Kelteseth
3 points
55 months ago

I have written a blog post about cmake for qmake people: https://screen-play.app/blog/qmake_to_cmake/

David Grayson
2 points
55 months ago

Could you please document the right way to compile the "host Qt" specified by -qt-host-path so we only build the needed tools like moc and uic, instead of compiling all of Qt? For anyone like me who only wants to cross-compile Qt and not have a host version, this will save us a lot of time.

nn jj
1 point
55 months ago

qmake was an excellent build system. but because of lacking of document and lots of undocumented hacks. I gradually switch to cmake..

Violet Giraffe
1 point
55 months ago

Cmake is one of the worst build systems I've ever seen as soon as you need to do anything beyond simply listing your headers, SRC and INCLUDEPATH. I hope qmake will continue on, perhaps someone will maintain its fork. Contrary to CMake, qmake is the easiest to use build system I know and it easily supports complicated configuration and a bit of scripting. I use it even for non-qt project for its simplicity and flexibility.

Richard Weickelt
3 points
55 months ago

Yes, the CMake language is pure horror, but I'd claim that adding own rules is not more difficult than with qmake.

Try Qbs. Its even more flexible and uses a much more powerful language than qmake: https://www.qt.io/blog/qbs-1-17-released

E
Energo Koder
0 points
55 months ago

I have created generic CMake scripts which allow to create C++ (with or without Qt) apps and libs very easily: https://gitlab.com/energokoder/energo-budowa

Daniel Nicoletti
0 points
55 months ago

Last time I tried CMake with Qt on iOS I couldn't get far, is it supported on iOS already? Are there some pages with tips?

J
Jörg Bornemann
1 point
55 months ago

Yes, it's supported. Maybe the "Cross compiling for iOS" section in qtbase/cmake/README.md is helpful.

Mykola Krachkovsky
0 points
55 months ago

Are this instructions still valid? I've downloaded full archive (http://download.qt.io/development_releases/qt/6.0/6.0.0-alpha/single/qt-everywhere-src-6.0.0-alpha.tar.xz) and after

export LLVM_INSTALL_DIR=/usr/lib64/cmake/clang

./configure -cmake -prefix ~/opt/qt6a

cmake --build . --parallel

got: ninja: error: loading 'build.ninja': No such file or directory

Also, test vulkan-server-buffer fails, because

#define VK_USE_PLATFORM_WAYLAND_KHR 1

#include <vulkan/vulkan.h>

also needs wayland-client.h but there is no dependency for wayland-client.

Mykola Krachkovsky
0 points
55 months ago

Solved problem by direct cmake call — everything is fine with build.ninja then (also made other build directory):

cmake -GNinja -DCMAKE_INSTALL_PREFIX=<prefix> ../qt-everywhere-src-6.0.0-alpha

offtopic: This commento plugin is buggy — editing ruins formatting — and cant reply to self.

J
Jörg Bornemann
-1 points
54 months ago

The instructions here are still valid. If you encounter a bug, please report it via the bug tracker.

Off-topic: Sorry for getting this late back to you. Apparently I'm not getting email notifications for comments. There seems to be something off with this commenting facility.

Andrei Cherniaev
0 points
52 months ago

It is the most important in Qt 6! Great job.

The are a lot of about cross-compiling. But as I know the most interesting in 2021 is compiling Qt on ARM64 directly. May be you add some unit-test of building system for OrangePi boards and etc?