Qt5 Build System
This is a description of the Qt5 qmake-based build system.
For Qt6, see Qt6 Build System.
For a description of terms used here, please see the Qt Build System Glossary.
Qt5 Configure System Gentle Introduction
Qt 5.10 introduced a new configuration system:
The goal of the new system is twofold. First of all, we are seeing a large need, especially with our embedded users, to customise how Qt is being built. Many of them have size restrictions or rather hard testing requirements, where being able to limit the functionality available in Qt is extremely helpful. We had such a system (see src/corelib/global/qfeatures.txt) before, but it was disconnected from the configure script, wasn’t really maintained since Qt 4 times, and had quite many limitations that I hope to solve with the new system.
Secondly, we have had to deal for a long time with two different configuration systems. We had a monster of a shell script for unix, and an Qt based executable on Windows. Changes to Qt often required changing both, and it was very easy to sneak subtle errors in. Maintainability of the old system was in general very poor.
- - Lars Knoll (on the Qt development mailing list)
Overview
The new system basically moves most of the configuration handling from the shell script over to a declarative json file (currently configure.json) that is being processed by qmake. The change aims to be a 1 to 1 (or at least as close as possible) mapping of the old shell script to the new system.
Detailed Description
Here’s a look at the details of the new system, in case you need to implement your own configure tests or want to add configurability to Qt.
We now have one global configuration file qtbase/configure.json, that contains much what the old configure.sh and configure.exe did. There is also a configure.pri file that contains some qmake methods to handle special cases (specialised tests our output handling). Individual modules also have separate configure.json and/or configure.pri files.
You can find the infrastructure for the whole system in qtbase/mkspecs/features/qt_configure.prf. The system basically works through two main systems.
- The first one is a callback infrastructure, where all “type” fields provided in the json file actually refer to callback functions in qmake that do the actual handling. This allows you to e.g. implement your own custom test function in a .pri file and refer to that from the json. qt_configure has callbacks for the most common tasks, so you won’t need this feature in most cases. If you want to know how such custom callbacks can look like, have a look at configure.pri.
- The other central piece is an expression evaluator, that is being used to evaluate conditions. It allows you to test for rather complex conditions (like e.g. “config.win32 && tests.mysql && features.opengl”) and is being used in many places. The evaluator understands boolean operators (!, && and ||), comparison operators (== and !=), braces and different types of arguments:
- true/false
- boolean values
- ‘foo’
- The string literal foo
- config.foo
- Variables in the qmake CONFIG var (usually used for platform dependent stuff like config.unix)
- tests.foo
- Boolean whether the test foo succeeded
- tests.foo.bar
- Variable bar, set by tests.foo
- features.foo
- boolean whether feature foo is available
- features.foo.bar
- variable bar set by features.foo
- arch.foo
- architecture (like x86, arm, etc)
- input.foo
- input variable (set by command line handling)
- far.foo
- Any variable set in qmake
- call.foo
- return value of the replace function foo in qmake
This gives you quite some options to define conditions and dependencies between features. The system automatically runs the configure test, if you ask for tests.foo, so dependency handling is fully transparent.
configure.json
Now let’s have a look at configure.json:
The file contains a couple of sections. The first one is called “files”, and contains definitions for the output files. You should not need to touch this one.
The next section contains the command line parameters that you can pass to the configuration system. It basically defines the valid command line arguments that configure will recognise. They are being mapped to a config.input.option value in qmake, that is then being used in the next step of defining the features we will use for Qt.
Typical entries looks like
"harfbuzz": { "type": "enum", "values": [ "no", "qt", "system" ] }, "headersclean": "boolean",
This means harfbuzz is an option that can take a selection of args (-no/-qt/-system), whereas headersclean is a boolean argument (-headersclean and -no-headersclean accepted). The second form is a shorthand for
"headersclean”: { “type”: “boolean” }
Note that the type variable refers to a callback. In this case a test function qtConfCommandline_boolean.
Then comes a section with tests. Those define all the configure tests that so far have been executed by the shell script. The definition of a typical test looks like:
"fontconfig": { "description": "Fontconfig”, "type": "compile”, "test": "unix/fontconfig”, "pkg-config-args": "fontconfig freetype2”, "libs": "-lfontconfig -lfreetype" }
This basically defines a configure test for fontconfig. It’s a compile test, the test being in config.tests/unix/fontconfig. It’ll try to use pig-config to determine the correct LIBS and CFLAGS to compile and link against the library, and there is a fallback for the libs in case fontconfig can’t be found.
Again, the type variable refers to a callback (qtConfTest_compile in this case).
After that we have the central section that defines all the features. Let’s take one example:
"icu": { "description": "ICU”, "autoDetect": "!config.win32”, "condition": "tests.icu”, "output": [ "publicQtConfig” ] },
This defines the icu feature. It’s not auto-detected on windows, requires the ice configure test to pass, and will then generate one output called publicQtConfig. Here are some details of the fields:
- description
- A short description of the feature. Used by the summary section below
- autoDetect
- Should evaluate to a boolean value whether to automatically detect the feature. Defaults to true
- emitIf
- Skip the feature completely if this evaluated to false (don’t evaluate conditions or outputs). Defaults to true.
- enable
- Evaluates to a condition that will enable the feature (defaults to “input.feature == yes”)
- disable
- the opposite, (defaults to “input.feature == no”)
- condition
- A condition that determines whether the feature is enabled/disabled. Will generate an error if it conflicts with the enable field above
- output
- Different types of output to generate. Also here you can define arbitrary callbacks, but the standard types should cover most needs:
- “publicQtConfig"
- Add the feature name to the QT_CONFIG variable in the public pri file (here qconfig.pri)
- “publicConfig"
- Add the feature name to the CONFIG variable in the public pri file (here qconfig.pri)
- "privateConfig"
- Same for the private pri (qmodule.pri)
- “feature”
- Defines a feature. Adds it to QT_CONFIG if available, sets QT_NO_FEATURE otherwise
{ “type”: “define”, “name”: “FOO”, “value”: “expression” } #define FOO expression in the public header file, expression is evaluated { “type”: “libs”, “test”: “configtest” }
Output QMAKE_LIBS/CFLAGS_FEATURE defining parameters required to use an external library
In addition, there are varAssign, varAppend and varRemove to assign, append and remove values from qmake variables
All outputs can have a ‘negative’: true/false field (default false), that would reverse when output is being generated (usually only if the feature is available, and a ‘condition’ field.
Finally there are two sections called ‘earlyReport’ and ‘report’. Use ‘report’ unless you know what you’re doing. These sections allow you to define conditions under which notes, warnings or errors are being reported back to the user.
Finally, there’s a summary section, that defines the configure summary we’re reporting back to the user. Lars leaves figuring out the details here as an exercise to the reader ;-)
Qt5 Configure System Reference
This is the reference documentation for the Qt5 configure system.
configure.json
There are several instances of this file
top-level - SOURCE_ROOT/configure.json
For the whole of Qt. Only defines the “skip” command line option at the moment.
repo level - SOURCE_ROOT/qtbase/configure.json
Valid for all module within this repository.
module level - SOURCE_ROOT/qtbase/src/network/configure.json
Valid for this particular module.
repo level keys
- files
- subconfigs
- commandline
- libraries
- testTypeDependencies
- testTypeAliases
- tests
- features
- earlyReport
- report
- summary
module level keys
- module: The current module’s name. E.g. “network”
- depends: list of module this module depends on
- testDir: path to the config.tests directory, relative to this file
- commandline: specification of command line arguments for configure
- libraries: definitions of libraries that can be used with QMAKE_USE
- tests: definition of configure tests
- features: Qt features that can be enabled/disabled
- condition: The module will be skipped if the condition is not met
- report: Notes and warnings that are displayed if certain conditions are met.
- summary: Defines what is displayed in configure’s summary output for this module.
commandline
This object defines the commandline options for the configure script.
custom
Here, we can register a custom commandline handler for configure arguments that do not fit into the rest of the declarative concept of configure.json. As of Qt 5.15, there can be exactly one custom commandline handler per
configure.json
. The custom handler is run before the actual command line processing. Example:
“commandline”: { “custom”: “qmakeArgs”, … }
will trigger the callback
defineTest(qtConfCommandline_qmakeArgs) { contains(1, QMAKE_[A-Z0-9_]+ *[-+]?=.*) { config.input.qmakeArgs += $$1 export(config.input.qmakeArgs) return(true) } return(false) }
which filters out command line arguments that assign to QMAKE_* variables and puts them into an internal variable.
options
The keys in the
options
objects are the arguments that can be passed to configure. With
“bindir”: “string”,
we can do
configure -bindir bin
. The values of the
options
object are strings or objects. If they are strings, the string specifies the
type
of the argument:
“bindir”: “string”,
is equivalent to
“bindir”: {“type”: “string”},
The value of this configure argument lands in
config.input.bindir
.
name
name
For command-line options that do not form a valid variable name, one can define the internal variable name like this:
"static-runtime": { "name": "static_runtime" },
The result ends up in
config.input.static_runtime
in this example.
type
type
An option of type
foo
triggers a callback
qtConfCommandline_foo
in
qt_configure.prf
or
configure.pri
. There are the following pre-defined types:
Type | Valid Values |
---|---|
boolean | yes, no |
enum | one of multiple defined values |
see below | |
string | non-empty string |
optionalString | “yes”, if empty string |
the string, otherwise | |
addString | non-empty string |
The argument can be passed multiple times. | |
The values is then a list of strings. | |
void | does not accept a value |
The type can be some arbitray string as illustrated by the type
redo
which is triggered by the
-redo
configure argument and calls
qtCommandline_redo
. That’s as customizable as it can get but is not to be confused with the
custom
command line option object above. The Gods of Naming Things Well just cried a bit. Enums have another key, named
values
, that can be a list of valid values:
“pcre”: { “type”: “enum”, “values”: [ “no”, “qt”, “system” ] },
But behold! We can also use a map as demonstrated by this little brain teaser:
“release”: { “type”: “enum”, “debug”, “values”: { “yes”: “no”, “no”: “yes” } },
values
values
Command-line options can specify a list of valid values.
“linker”: { “type”: “optionalString”, “values”: [ “bfd”, “gold”, “lld” ] },
If any other value is passed, an configure exits with an error.
prefix
prefix
This fantastically named commandline key is best described by an example:
"prefix": { "D": "defines", }
What this does is: enable the user to to
configure -DFOO -DBAR
and stores the list
FOO BAR
in
config.input.defines
. Internally, this creates a command-line option of type
addString
.
assignments
assignments
Here we can define variables assignments the user can pass to configure.
With
“assignments”: { “MYSQL_PATH”: “mysql.prefix” },
we can do
configure MYSQL_PATH=/some/where
. The result is stored in
config.input.mysql.prefix
.
Every assignment that is not registered in this way will fail:
./configure NO=WAY ... ERROR: Assigning unknown variable 'NO' on command line.
Note that variables beginning with
QMAKE_
are handled by
qtConfCommandline_qmakeArgs
as described above.
libraries
Is an object. Its keys are strings that can be passed to QMAKE_USE. Its values are objects again.
Example:
"libraries": { "corewlan": { "label": "CoreWLan", "export": "", "test": {"lang":"objc++","include": [ "CoreWLAN/CoreWLAN.h", "CoreWLAN/CWInterface.h" ], "main":"[CWInterface interfaceWithName:@"en2"];" }, "sources": [ "-framework CoreWLAN -framework Foundation" ] }, … }
- label: The display name for configure’s user output.
- export:
- test: the name of a test or an object defining a configure test for this library. Details below.
- sources: Array of several “sources” where the library might be found.
- headers: Array of string, containing relative paths to header files (as they would appear in #include directives). Configure tries to locate those headers using heuristics. If the headers cannot be located, the test fails. The generated test source code will contain include directives for these headers on success. Note that header files within frameworks cannot be located as of now. Use test.include instead.
test
Configure test for the library this object is located in.
If
test
is a string, then it’s the name of a subdirectory below config.tests containing a test project that is configured with qmake and then built. If
test
is an object, then the following keys are used:
- lang: The programming language to be used. Valid values are [“c++”, “c”, “objc”, “objc++”]. Default is “c++”.
- head: array of strings with custom code before includes
- include: array of strings for which include directives are generated. E.g. produces
["vector"]
.#include <foo.h>
- tail: array of strings with custom code after includes
- main: Array of strings that form the body of the main function of the test. The main function’s signature for C++ is the usual .
int main(int argc, char **argv)
- qmake: Array of strings with custom code for the test’s .pro file. Added after SOURCES. The special string is replaced by the current config dir.
@PWD@
sources
A source is a generalized location of a library. Configure probes the sources sequentially. The first successful wins.
There are different types of sources: - inline: the library is specified inline in a ‘libs’ field. Overrides from the command line are accepted. Where “inline” means “this is just a string”. But hey, this can also be an object. - makeSpec: The library is provided by the qmake spec. This source type cannot fail. - pkgConfig: The library is found via pkg-config.
Sources are dispatched by type to
qtConfLibrary_$${type}
. The handlers for the types above are defined in qt_configure.prf. Modules can add their own type “foo” by adding
defineTest(qtConfLibrary_foo)
to their local configure.pri. The common keys of a source object are: - type: The type as described above. - condition: If the condition is false, the source fails. Usually some platform or toolchain check like
"config.msvc"
.
inline sources
Can be specified “inline” as string
“sources”: [“-lz -lproxy”]
or as object if you need to set additional properties
“sources”: [ { “comment”: “longwinded rambling”, “libs”: “-lz -lproxy” }]
With
builds
you can specify different libraries per build:
{ "libs": "", "builds": { "debug": "-ldbus-1d", "release": "-ldbus-1" }, "condition": "config.win32" },
makeSpec sources
"sources": [ { "type": "makeSpec", "spec": "NETWORK" } ]
Reads the variables QMAKE_LIBDIR_NETWORK, QMAKE_LIBS_NETWORK and QMAKE_INCDIR_NETWORK from the mkspec. This source is always successful.
pkgConfig sources
{ "type": "pkgConfig", "host": true, "args": "dbus-1 >= 1.2" },
- host: boolean, run pkgConfig for host or target?
- args: Arguments for pkgConfig.
tests
TO BE DONE
features
Selection
Features can be either on or off. The following keys control the state of a feature. They are listed in order of significance.
- emitIf:
- Defaults to true
- Early check (checked before anything else)
- Overrules every other option and will make the feature non-existent, if false. Note: this means that before using such a feature, you must replicate the condition of emitIf to avoid “feature X does not exist” errors.
- If used in connection with enable or disable and evaluates to false: Will cause a warning, feature will be disabled
- Commonly used to turn off features, that are not supported on certain platforms (Direct… on Linux)
- enable/disable:
- Default to false
- Operate on input variables (corresponds to command line options : -feature-foo, -no-feature-foo, and command line options defined in configure.json)
- Override autoDetect
- If both are set to true, “disable” wins
- If enable is true and “condition” is not met, will trigger an error
- Used to explicitly enable/disable a feature from the command line
- autoDetect:
- Defaults to true
- Only relevant if the feature is not enabled/disabled on the command line
- If true: feature is enabled by default. But the condition decides about feature’s status. It’s not an error if the condition is false.
- If false: feature is disabled by default. It can only be turned on manually with the -feature-foo configure switch.
- condition:
- Defaults to true
- Requirements that have to be met in order to use this feature
Output
The following options control where and how feature values are written to.
Also here you can define arbitrary callbacks. The callbacks start with qtConfOutput_ and here are the ones that are defined in qt_configure.pri:
publicQtConfig
Add the feature name to the QT_CONFIG variable in the public pri file.
publicConfig
Add the feature name to the CONFIG variable in the public pri file.
privateConfig
Add the feature name to the CONFIG variable in the private pri file.
define
Add a preprocessor define to the public header file.
Example:
{ "type": "define", "name": "QT_REDUCE_RELOCATIONS" }
The define can be negated, i.e. it is only set if the feature is off.
Example:
{ "type": "define", "negative": true, "name": "QT_NO_WIDGETS" }
feature
Defines a feature. Adds it to QT_CONFIG if available.
Otherwise, sets the
QT_NO_xxx
define in the public header.
publicFeature
Set the
QT_FEATURE_xxx
define in the public header.
privateFeature
Set the
QT_FEATURE_xxx
define in the private header.
varAssign
Set a variable in the public or private module pri file.
{ "type": "varAssign", "public": true, "name": "QT_DEFAULT_QPA_PLUGIN", "value": "tests.qpa_default_platform.plugin" }
varAppend
Append a value to a variable in the public or private module pri file.
varRemove
Remove a value from a variable in the public or private module pri file.
testTypeDependencies
From the introducing commit c8b46d3989ded1368d40e487a141b3cfb3d968ba:
Some test types (like the compile tests) require that other features have been checked before, so that the compile test sets up the right environment. Implement this through a ‘testTypeDependencies’ section in the json file that explicitly encodes those dependencies for certain test types.
Example from qtbase:
“testTypeDependencies”: { “linkerSupportsFlag”: [ “use_gold_linker” ], “compile”: [ “shared”, “use_gold_linker”, “compiler-flags”, “gcc-sysroot” ], “detectPkgConfig”: [ “cross_compile” ], “library”: [ “pkg-config” ], “getPkgConfigVariable”: [ “pkg-config” ], “neon”: [ “architecture” ] },
The keys are test types. The values are lists of features.
testTypeAliases
Comment from the source code: >>> Test type aliasing means that one test type’s callback is called by another test type’s callback. Put differently, one callback forwards the call to another one. The former representation is more natural (and concise) to write, while the latter is more efficient to process. Hence, this function inverts the mapping. >>>
Example from qtbase:
“testTypeAliases”: { “compile”: [ “library”, “architecture”, “x86Simd”, “x86SimdAlways” ] },
The
architecture
test calls
qtConfTest_compile
and inspects the build result. The type
architecture
is declared as alias for
compile
. The
testTypeAliases
data is used together with
testTypeDependencies
to calculate the order in which the tests are run / features are evaluated. Putting everything together, in our example the
architecture
test must run after the features
shared
,
use_gold_linker
,
compiler-flags
and
gcc-sysroot
have been evaluated.
summary
TO BE DONE
configure.pri
Optional per module. This file is an extends qt_configure.prf. For example, in QtNetwork configure.pri defines qtConfLibrary_openssl which makes it possible to have libraries of type openssl in that module.
special qmake variables
There are certain qmake variables that only make sense in the context of the Qt build itself. Other qmake variables behave specially in the Qt build.
Public and private modules
A normal module (e.g. QtNetwork) is split into a public and a private module. Usually, users depend on the public one:
QT += network
but you can also depend on the private one. Some tests do that, for example.
QT += network-private
The meaning of
QT
within Qt modules is overloaded.
Dependencies between Qt modules
With the following variables one can express dependencies between Qt modules:
Variable | Meaning |
---|---|
QT
|
Public dependencies of the public module. |
QT_PRIVATE
|
Private dependencies of the public module. |
QT_FOR_PRIVATE
|
Public dependencies of the private module. |
Public dependencies means the dependencies are exported. Private dependencies are not exported.
See the message of commit b8bee40726fc93db9a025324712de6fff6a084d5 for more information.
How to
Check for features
In .pro files
To check for a certain feature use the qtConfig test function:
qtConfig(icu): DEFINES += WE_USE_ICU
There are two ways of making a module’s (private) features available to your pro file: - add <module> (or it’s “-private” equivalent for private features) to QT - add <module> (or it’s “-private” equivalent for private features) to QT_FOR_CONFIG (this only adds the feature related functionality without private include paths)
In C++ code
To check for a certain feature use the QT_CONFIG macro. For example, to check for the private feature
icu
, do the following:
#if QT_CONFIG(icu)
doStuffWithFoo();
#endif
This will only work correctly if the
QT_FEATURE_icu
macro is defined. It is defined in
qtcore-config_p.h
. Add the include
qglobal_p.h
for private features of QtCore. This file is also pulled in by any private header. If you already include other private headers from QtCore you may omit the include. Public features are defined in
qtcore-config.h
. Just include any QtCore header (or
qglobal.h
) to be able to check for those. The respective include files for other modules, e.g. QtNetwork, are called
qtnetworkglobal[_p].h
.