Qt5 Build System

From Qt Wiki
Jump to navigation Jump to search


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.

  1. 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.
  2. 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

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

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

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

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

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.
    ["vector"]
    
    produces
    #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
    @PWD@
    
    is replaced by the current config dir.

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.

Qt modules related variables

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

.