Qt Build System Glossary: Difference between revisions
mNo edit summary |
No edit summary |
||
Line 491: | Line 491: | ||
====If Qt fails to configure==== | ====If Qt fails to configure==== | ||
Follow the steps at [[# | Follow the steps at [[#Qt configuration quick troubleshooting recipe|Qt configuration quick troubleshooting recipe]] and share the generated files. | ||
====If Qt fails to build==== | ====If Qt fails to build==== | ||
Line 499: | Line 499: | ||
====If a CMake project fails to configure==== | ====If a CMake project fails to configure==== | ||
Share your project | Share your project files, follow the steps at [[#CMake project quick troubleshooting recipe|CMake project quick troubleshooting recipe]] and share the generated files. | ||
====If a qmake project fails to configure==== | ====If a qmake project fails to configure==== | ||
Share your project .pro/pri files, follow the steps at [[#qmake trace file|qmake trace file]], share the trace file and the stdout of running qmake. | Share your project .pro/pri files, follow the steps at [[#qmake trace file|qmake trace file]], share the trace file and the stdout of running qmake. |
Revision as of 09:12, 8 June 2023
To be on the same page when talking about Qt build system related topics, we're offering here a collection of used terms.
Build System
The build system of Qt is the collection of files that describe how to build Qt from source.
This includes, among others, the project files and all configure-related files.
It does not include QMake, CMake, make or ninja.
Build Tool
In the widest sense, those are tools that contribute to the build.
Common build tools are ninja, make, nmake and jom.
We can count build system generators to the the build tools.
Build System Generator
Build system generators are tools that take a high-level project description as input (e.g. a qmake project file) and output a lower level build system (e.g. a Makefile). Examples are QMake, CMake, gyp or gn.
In-source Build
This means that your build directory is the same as your source directory. This is also called in-tree build.
$ cd qt-source-dir
$ ./configure
$ cmake --build .
$ cmake --install .
Out-of-source Build
This means that your build directory is outside your source directory.
$ mkdir qt-build-dir
$ cd qt-build-dir
$ ../qt-source-dir/configure
$ cmake --build .
$ cmake --install .
This has the advantage that you can have multiple builds of the same source.
Also, it's very easy to do a fresh build by purging your build directory.
Shadow Build
This is just another name for out-of-source build.
Developer Build
A developer build of Qt is meant for developers of Qt.
It differs from a non-developer build in two details:
- It's a non-prefix build if the -prefix configure option is omitted. See below for non-prefix builds.
- It enables private tests. Those are autotests that require symbols to be exported that would be hidden under normal circumstances.
$ mkdir qt-build-dir
$ cd qt-build-dir
$ ../qt-source-dir/configure -developer-build
$ cmake --build .
It's perfectly possible to have a developer prefix build:
$ mkdir qt-build-dir
$ cd qt-build-dir
$ ../qt-source-dir/configure -developer-build -prefix ~/Qt
$ cmake --build .
$ cmake --install .
Prefix Build
This means that you configured Qt with an installation prefix.
$ mkdir qt-build-dir
$ cd qt-build-dir
$ ../qt-source-dir/configure -prefix /opt/qt6
$ cmake --build .
$ cmake --install .
Prefix builds require that you install after building.
Note that, by default, configure uses /usr/local as installation prefix. Unless -developer-build is specified. See below.
Non-prefix Build
This means that you configured Qt without an installation prefix.
This is the default for developer builds.
$ mkdir qt-build-dir
$ cd qt-build-dir
$ ../qt-source-dir/configure -developer-build
$ cmake --build .
There's no point in installing a non-prefix build.
Technically, the install prefix is set to the build dir (or <build-dir>/qtbase for a top-level build), and care is taken by the build system that the necessary files are copied/built into the right place.
For non-developer builds, one can force a non-prefix build by setting the prefix explicitly to the build directory. For a top-level build, one would do:
$ ../qt-source-dir/configure -prefix <full-path-to-build-dir>/qtbase
For a build of qtbase only, one would do:
$ ../qt-source-dir/qtbase/configure -prefix <full-path-to-build-dir>
Top-level Build
As you probably know, Qt is split into several repositories: qtbase, qtdeclarative, qttools and so on. There's also a "super" repository called "qt5" that bundles everything as git submodules.
A top-level build means building the "super" repository.
Dependencies between the single repositories are automatically resolved by the build system.
Per-repository Build
This is building Qt's single repositories one by one.
Dependencies have to be resolved by the user doing the build.
Per-repository builds can be configured as either prefix or non-prefix builds.
An out-of-source non-prefix build example:
$ mkdir qtbase-build-dir
$ cd qtbase-build-dir
$ ../qtbase-source-dir/configure -developer-build
$ cmake --build .
$ cd ..
$ mkdir qtdeclarative-build-dir
$ cd qtdeclarative-build-dir
$ ../qtbase-build-dir/bin/qt-configure-module ../qtdeclarative
$ cmake --build .
# Repeat for other needed repositories
An out-of-source prefix build example:
$ mkdir qtbase-build-dir
$ cd qtbase-build-dir
$ ../qtbase-source-dir/configure -prefix /opt/qt6
$ cmake --build .
$ cmake --install .
$ cd ..
$ mkdir qtdeclarative-build-dir
$ cd qtdeclarative-build-dir
$ /opt/qt6/bin/qt-configure-module ../qtdeclarative
$ cmake --build .
$ cmake --install .
# Repeat for other needed repositories
debug-and-release build
This means that Qt (or some other project) is configured for building the Debug and Release configurations in the same build directory.
QMake has in-built support for that. Out of the box, two fixed configs are available: debug and release.
For CMake projects, Multi-Config generators (e.g. Ninja Multi-Config) support the build of multiple configurations per build directory.
Framework Build
On Apple platforms, the Qt libraries are built by default as frameworks.
The <install-prefix>/lib/Qt6Core.framework then contains the library and all related header files.
Qt can also be configured as non-framework build by passing -no-framework to configure.
In this case, <install-prefix>/lib contains the libraries as .dylib files, and the header files are placed below <install-prefix>/include.
Documentation-only Build
Packagers usually build Qt per repo, but the documentation must be built from the full Qt source archive to ensure that inter-repo links are set up correctly. If building the documentation for a single repo, you can consider setting the QDOC_NOLINKERRORS environment variable to suppress QDoc's "Can't link to" warnings. When building the documentation, it's desirable to avoid building tools and re-use to tools of an existing Qt build.
Prior to configuring, make sure you have LLVM/libclang v10.0+ installed and LLVM_INSTALL_DIR set to the base of the installation. This is required to build the qdoc tool which is required for building documentation.
One now can do a documentation-only build by configuring Qt like this:
cmake /path/to/Qt/source -GNinja -DQT_HOST_PATH=/path/to/Qt/installation ...other flags...
and build the documentation with
ninja docs
Introduced in https://codereview.qt-project.org/c/qt/qtbase/+/346155
To just build the documentation for one module after you have built it for all modules once:
ninja docs_<module_name>
For example, to build the docs for the qtdoc module, enter:
ninja docs_qtdoc
Qt tests
Starting with Qt 6, Qt's tests can be built in a few ways:
- all tests defined in a repository are built in-tree with the Qt build directory (henceforth called 'in-tree')
- all tests defined in a repository are built in a standalone build directory (called 'standalone tests')
- one specific test is built in a standalone build directory (called 'standalone test')
In-tree tests
To build tests in-tree with the Qt build itself, you need to pass the following configure argument when configuring Qt
$ mkdir qt-build-dir
$ cd qt-build-dir
$ ../qt-source-dir/configure -make tests
$ cmake --build .
$ ctest -V # to run all tests
The advantage of this approach is that you can build and run the tests from the same build directory as your Qt build directory. The disadvantage is that configuration time and total build time will be longer.
To build and run one single test, use
$ ninja tst_qprocess_check
To run tests matching a certain name, use
$ ctest -V -R ^tst_qprocess
To run all tests in a subdirectory, use
$ ctest -V -L tests/auto/corelib
Standalone tests
To build standalone tests in a separate build directory you first need to build at least qtbase, and then you need to use a special shell script provided by the qtbase installation:
$ mkdir qt-build-dir
$ cd qt-build-dir
$ ../qt-source-dir/configure -developer-build -nomake tests
$ cmake --build .
$ cd ..
$ mkdir qtbase-standalone-tests
$ cd qtbase-standalone-tests
$ ../qt-build-dir/libexec/qt-internal-configure-tests ../qt-source-dir
$ cmake --build .
$ ctest # to run the tests
The advantage of this approach is that you can build and run the tests from a separate build directory, without affecting overall Qt build times. It also circumvents some catch-22 issues regarding qmlimportscanner and QML modules. This approach is what Qt's CI is currently using. The disadvantage is that you have to go through a few steps more than usual.
Note that qt-internal-configure-tests might be located in the "bin" directory instead of "libexec" (on Windows hosts and in older Qt versions).
Single standalone test
Sometimes you just want to build one single test, outside your Qt build directory. You can do that with a special 'qt-cmake-standalone' shell script.
$ mkdir qt-build-dir
$ cd qt-build-dir
$ ../qt-source-dir/configure -developer-build
$ cmake --build .
$ cd ..
$ mkdir tst_qwidget-build-dir
$ cd tst_qwidget-build-dir
$ ../qt-build-dir/libexec/qt-cmake-standalone-test ../qt-source-dir/tests/auto/widgets/kernel/qwidget
$ cmake --build .
$ ctest # to run the test
The advantage of this approach is that the configuration time and build time of one test is very fast.
Note that qt-cmake-standalone-test might be located in the "bin" directory instead of "libexec" (on Windows hosts and in older Qt versions).
test batching
On some platforms, for example WASM, linking is slow and/or the resulting binaries are huge. Test batching can be enabled to mitigate these problems: multiple tests are built into one binary.
See https://codereview.qt-project.org/c/qt/qtbase/+/421446 which introduced this feature.
qmake trace file
A qmake trace file for a qmake-based project is produced by passing three -d arguments to qmake:
$ qmake /my/project/sources -d -d -d >qmake.trace 2>&1
CMake trace file
A CMake trace file for a CMake-based project is produced like this:
$ cmake /my/project/sources --trace-expand --trace-redirect=cmake_trace.txt --log-level=STATUS
For a Qt build you would do
$ configure ...more options... -- --trace-expand --trace-redirect=cmake_trace.txt --log-level=STATUS
CMake find_package troubleshooting
To make CMake output where it looks for files when running commands like find_package / find_path / find_library run:
$ cmake /my/project/sources --debug-find
For a Qt build you would do
$ configure ...more options... -- --debug-find
This will generate a lot of output. To redirect it into a file use:
$ cmake /my/project/sources --debug-find > config_debug.txt 2>&1
This works across bash, powershell and batch files.
CMake try_compile troubleshooting
Starting with CMake version 3.26, cmake writes a result log of running try_compile-like calls to
$ cat <build-folder>/CMakeFiles/CMakeConfigureLog.yaml
When a try_compile project fails for some reason, you can reconfigure the project with
$ cmake /my/project/sources --debug-trycompile
This will store the generated try_compile projects in a subdirectory under CMakeFiles/CMakeScratch/ where you can inspect it, including trying to rebuild it.
CMake project quick troubleshooting recipe
Run:
$ cmake /my/project/sources --trace-expand --trace-redirect=cmake_trace.txt --log-level=STATUS --debug-trycompile --debug-find > config_debug.txt 2>&1
And share the zipped contents of:
cmake_trace.txt
config_debug.txt
CMakeCache.txt
CMakeFiles/CMakeConfigureLog.yaml
Qt configuration quick troubleshooting recipe
Run:
$ configure ...more options... -- --trace-expand --trace-redirect=cmake_trace.txt --log-level=STATUS --debug-trycompile --debug-find > config_debug.txt 2>&1
or
$ qt-configure-module ...more options... -- --trace-expand --trace-redirect=cmake_trace.txt --log-level=STATUS --debug-trycompile --debug-find > config_debug.txt 2>&1
And share the zipped contents of:
cmake_trace.txt
config_debug.txt
CMakeCache.txt
CMakeFiles/CMakeConfigureLog.yaml
Repo Target Sets
A repo target set is a named set of targets within a Qt repository. One can build a repo target set by passing QT_BUILD_SINGLE_TARGET_SET=target-set-name to cmake when configuring the Qt repository.
The concept was introduced by this change for aiding building conan packages, but it may be useful for any kind of Qt distribution that wishes to create packages of a finer granularity than Qt repository level.
Repo target sets are defined in the top-level CMakeLists.txt of a repository. The definitions must be followed by a call to qt_internal_prepare_single_repo_target_set_build().
Let's walk through what has been done for the qtscxml repository.
# CMakeLists.txt
# Define the repo target set "qtscxml".
# That matches the conan package name but can be an arbitrary string.
qt_internal_define_repo_target_set(qtscxml)
# Define the repo target set "qtscxmlqml".
# The target ScxmlQml will be part of this set, and building it requires a call to
# find_package(Qt6 COMPONENTS Scxml).
# Denote this requirement with the DEPENDS argument.
qt_internal_define_repo_target_set(qtscxmlqml DEPENDS Scxml)
qt_internal_prepare_single_repo_target_set_build()
By now, all those repo target sets contain all targets of the repository. We now assign every module, plugin and tool to its target set.
# src/scxml/CMakeLists.txt
qt_internal_include_in_repo_target_set(qtscxml)
qt_internal_add_module(Scxml
...
# src/scxmlqml/CMakeLists.txt
qt_internal_include_in_repo_target_set(qtscxmlqml)
qt_internal_add_qml_module(ScxmlQml
...
qt_internal_include_in_repo_target_set operates on file level and simply skips the rest of the file if we're currently building a repo target set that does not match.
Apps and tools shipped by Qt
https://bugreports.qt.io/browse/QTBUG-100047
Qt ships a variety of executables(tools and apps) that vary by the following parameters:
- user-facing vs. internal
- maybe built when cross-compiling
- maybe creates a target in host Qt that can be imported in target Qt
- maybe required in host Qt when building target Qt
They can broadly be categorised into the following groups:
- build tools - they contribute to the Qt build, e.g code generators (moc, uic, cmake_automoc_parser, qsb, repc, qmltc, qmlcachegen, qmlimportscanner, qdoc?, lupdate?, lrelease?, qvkgen) these usually go into $prefix/libexec, unless they are also user-facing, which means they'd go into $prefix/bin
- target apps (qml, qmljs, qwebengineprocess), they don't contribute to the build and are also built for the host, but are intended to run on the target platform / device
- host-only development tools (qmake, qmllint?, qmlformat, qmldom, qmljsrootgen, qwebengine_convert_dict?, Linguist, Assistant, windeployqt, macdeployqt, androiddeployqt) usually used for development of Qt projects
Tools should be added using qt_internal_add_tool. They are usually built as part of the host build (aside from exceptions like windeployqt when cross-compiling from Linux to Windows). They will also be built during a target build, but only if -DQT_BUILD_TOOLS_WHEN_CROSSCOMPILING=ON is passed. A typical case when this is useful is for creating a Yocto SDK where a user might want to be able to build Qt code directly on the device.
Apps should be added with qt_internal_add_app.
CMake verbosity
Starting with Qt 6.2.4 and Qt 6.3.0, a non-developer-build of Qt by default sets CMAKE_MESSAGE_LOG_LEVEL to NOTICE. To restore the more verbose output either configure Qt with -DCMAKE_MESSAGE_LOG_LEVEL=STATUS or --log-level=STATUS.
$ cmake /my/project/sources --log-level=STATUS
$ configure ...more options... -- --log-level=STATUS
https://cmake.org/cmake/help/latest/variable/CMAKE_MESSAGE_LOG_LEVEL.html
Reporting build system issues
If you encounter an issue building Qt or your own project that uses Qt, please provide the following general information as well as any additional information as requested below:
- Operating system used:
- Target platform:
- Qt configure line / project configure line:
- Qt version / branch:
- CMake version:
- Ninja version:
- qmake version:
- Compiler version:
- macOS / iOS Xcode version:
- Android NDK / SDK version:
- WebAssembly Emscripten version:
Ninja: somethings rebuilds when it shouldn't
Try to replicate the scenario one more time, but make sure to add the -d explain flag when calling ninja. Share the standard output.
If Qt fails to configure
Follow the steps at Qt configuration quick troubleshooting recipe and share the generated files.
If Qt fails to build
Share the standard output of the build failure and the full command line of the failed build step.
If a CMake project fails to configure
Share your project files, follow the steps at CMake project quick troubleshooting recipe and share the generated files.
If a qmake project fails to configure
Share your project .pro/pri files, follow the steps at qmake trace file, share the trace file and the stdout of running qmake.