The role of qmake in Qt configure

We’ve known the role of the configure script in configuring Qt for build. The last step of the configure script is to call qmake.exe to complete the remaining configuration. The main configuration task is accomplished by qmake. The qmake.exe is called in c:\qtbuild\ as (assuming you did not provide any option to configure.bat):

c:\qtbuild\qtbase\bin\qmake.exe c:\Qt5.12.1\5.12.1\Src —

qmake is a very complex program. In fact, without trial and error, you even cannot find its usage or help information. It is unlike a windows application that uses /?, /h, /help, etc. to display the help information, but uses the option -help to show the usage:

From its usage doc, you can know it will find a .pro file in c:\Qt5.12.1\5.12.1\Src to process. That project file is c:\Qt5.12.1\5.12.1\Src\ We’ll look through this file to see what it does.

The first non-comment line of is an undocumented function:

cache(, super)

This function will create an empty cache file c:\qtbuild\.qmake.super. This is called super cache and modules will add themselves to it.

!QTDIR_build: cache(CONFIG, add, $$list(QTDIR_build))

This statement checks if QTDIR_build is in the CONFIG list. If not, create a cache file c:\qtbuild\.qmake.cache and add the following line to it:


Next, it reads c:\Qt5.12.1\5.12.1\Src\.gitmodules. This file contains the information of all submodules. For example, the information for the qtbase sub-module is:

[submodule “qtbase”]
path = qtbase
url = ../qtbase.git
branch = 5.12.1
status = essential
repoType = inherited

qmake will add the module name to the modules variable and add the properties of the module to the variables as module.qtbase.path=qtbase, module.qtbase.url=../qtbase.git, module.qtbase.branch=5.12.1, etc. The important properties of a module are project, depends, recommends. The project property of module is the .pro file name of the project. The depends/recommends property of module is the list of modules the module depends on. For example,

[submodule “qtapplicationmanager”]
depends = qtbase qtdeclarative qtwayland qtandroidextras
path = qtapplicationmanager
url = ../qtapplicationmanager
branch = 5.12
status = addon
project =

The qtapplicationmanager module depends on four other modules: qtbase qtdeclarative qtwayland qtandroidextras.


To understand the following code in the .pro file, you need to know what $$PWD and $$OUT_PWD are. $$PWD is the directory where the current .pro file resides(C:/Qt5.12.1/5.12.1/Src), while $$OUT_PWD is the current directory, i.e., the directory in which you run qmake.exe(C:/qtbuild).

qmake checks its arguments. If you provides a -skip modulename, the module will be added to the QT_SKIP_MODULES list.

For every module, qmake construct an object $$mod with several properties: $${mod}.subdir, $${mod}.file(the module’s .pro file), $${mod}.makefile(Makefile), $${mod}.target(module-modulename), $${mod}.depends(the module’s dependent modules).

The module names correspond to sub-directories in c:\Qt5.12.1\5.12.1\Src\, and are added to SUBDIRS in their dependent order, i.e., the later modules in SUBDIRS depend on the earlier modules in SUBDIRS. Since uses subdirs template, the modules specified by SUBDIRS are processed in their dependent order.

It seems qmake has not done anything essential till now. Do not worry. The last line of is


The load function will load a feature file: qt_configure.prf. The location of qt_configure.prf is c:\Qt5.12.1\5.12.1\Src\qtbase\mkspecs\features\. See this document for how qmake searches for feature files.

A feature file is like a .pro file. What is in a .pro file can also appear in a feature file. So we will look through qt_configure.prf , the supplemental file for

In qt_configure.prf, qmake creates a variable for current target qt and every sub-module in c:\Qt5.12.1\5.12.1\Src\: config.qt.dir, config.qtbase.dir, config.qtnetworkauth.dir, etc. Their values are set to c:\Qt5.12.1\5.12.1\Src, c:\Qt5.12.1\5.12.1\Src\qtbase, c:\Qt5.12.1\5.12.1\Src\qtnetworkauth, etc. All the names of qt and its sub-modules are added to the variable cfgs. If there is a file called configure.json in a subdir, the module name is added to configsToProcess.

Now, qmake loads another feature: configure_base.prf. This file mainly creates a variable QMAKE_MAKE_NAME=mingw32-make and QMAKE_MAKE=”set MAKEFLAGS=& mingw32-make”, based on the value of the environment variable $$MAKE.

The control flow goes back to qt_configure.prf. Now, another sub-module builtins is added to configsToProcess, and also config.builtins.dir is created which has the value c:\Qt5.12.1\5.12.1\Src\qtbase\mkspecs\features\data(there does exist a configure.json file in this directory). Every configurable sub-module has a configure.json and a possibly configure.pri in its (sub)directory. qmake will parse configure.json and load configure.pri.

Let’s see how qmake parses configure.json.

parseJson(configure_data, $$currentConfig)

configure_data is what’s read from configure.json. The parseJson built-in function parses the JSON string to $$currentConfig(e.g., config.builtins). Now, config.builtins not only has the member dir but also other members(keys) such as files, commandline, testTypeDependencies, features, etc. The keys in  configure.json now become the members in config.builtins, and the values in configure.json become the values of these members.  config.builtins._KEYS_ is the list of keys in configure.json. If configure.json has other keys than subconfigs, the module config (config.builtins) will be added to allConfigs .

If configure.json has a subconfigs key, the current module has children modules, i.e., the subdir corresponding to current module has sub-subdirs corresponding to its children. And the config for this module also has children configs. For example, config.builtins may have children configs: config.builtins_child1, config.builtins_child2, etc. child1, child2, etc are the keys under the subconfigs key. These sub-configs are prepended to configsToProcess and will be processed in the next loop.

I almost get crazy about the config variables qmake creates for modules till now:, configsToProcess, allConfigs, etc. Remember, configsToProcess is a module name list, and those modules have configure.json in their directories. config.xxxs are complete for all modules regardless they have configure.json or not. config.xxxs even include subconfigs. allConfigs are the list of config.xxxs that have configure.json and if the configure has something else besides subconfigs. For all modules in configsToProcess , qmake will load their configure.pri if they have. If a module in configsToProcess  has a libraries key, qmake will call qtConfSetupLibraries and qtConfSetModuleName to set the libraries up.

qtConfSetupLibraries creates a set of variables according to the content of the libraries key in configure.json. Let’s take qtbase module for an example as it has libraries:

"libraries": {
     "zlib": {
         "label": "zlib",
         "test": {
             "main": [
                 "z_streamp stream = 0;",
                 "(void) zlibVersion();",
                 "(void) compress2(0, 0, 0, 0, 1);  // compress2 was added in zlib version 1.0.8"
         "headers": "zlib.h",
         "sources": [
             { "libs": "-lzdll", "condition": "config.msvc" },
             { "libs": "-lzlib", "condition": "config.msvc" },
             { "libs": "-lz", "condition": "!config.msvc" },
             { "libs": "-s USE_ZLIB=1", "condition": "config.wasm" }
     "dbus": {
         "label": "D-Bus >= 1.2",
         "test": {
             "main": "(void) dbus_bus_get_private(DBUS_BUS_SYSTEM, (DBusError *)NULL);"
         "headers": "dbus/dbus.h",
         "sources": [
             { "type": "pkgConfig", "args": "dbus-1 >= 1.2" },
                 "libs": "",
                 "builds": {
                     "debug": "-ldbus-1d",
                     "release": "-ldbus-1"
                 "condition": "config.win32"
             { "libs": "-ldbus-1", "condition": "!config.win32" }
     "host_dbus": {
         "label": "D-Bus >= 1.2 (host)",
         "export": "",
         "sources": [
             { "type": "pkgConfig", "host": true, "args": "dbus-1 >= 1.2" },
             { "libs": "", "comment": "placeholder for DBUS_HOST_PATH" }
     "libudev": {
         "label": "udev",
         "test": {
             "main": "udev_unref(udev_new());"
         "headers": "libudev.h",
         "sources": [
             { "type": "pkgConfig", "args": "libudev" },


Specifically, for each library in libraries, it creates these variables(take the dbus lib for an example):

  • config.qtbase.libraries.dbus.export, if there is no such variable, i.e., there is no “export” key under dbus. The value of this variable is set to dbus.
  • config.qtbase.libraries.dbus.alias, which is set to dbus too.
  • config.qtbase.exports._KEYS_, and every lib name is added to it.
  • config.qtbase.exports.dbus, whose value is set to the alias dbus.

A library may have several sources. For each source, create the following variables:

  • config.qtbase.libraries.dbus.sources.0.library, which is set to the lib name dbus too.

Every source must have a type, if not, set its type to inline.

For every lib in the libs, create the following variables(take dbus for example):

  • config.qtbase.commandline.assignments.DBUS
  • config.qtbase.commandline.assignments.DBUS_PREFIX=dbus.prefix
  • config.qtbase.commandline.assignments.DBUS_INCDIR=dbus.incdir
  • config.qtbase.commandline.assignments.DBUS_LIBDIR=dbus.libdir
  • config.qtbase.commandline.assignments.DBUS_LIBS=dbus.libs
  • config.qtbase.commandline.assignments.DBUS_LIBS_DEBUG=dbus.libs.debug
  • config.qtbase.commandline.assignments.DBUS_LIBS_RELEASE=dbus.libs.release

These commandline.assignments are used to generate config.input.xxxx variables in the following qtConfParseCommandLine function.

Next, qtConfParseCommandLine function parse the command line and generate input variables in config.input.

Now, it you typed the configure command as:

configure.bat -list-features

qmake will list all the features and their simple introduction. You can enable/disable features in the command line.

If you typed the configure command as:

configure.bat -list-libraries

qmake will display all the libraries below their respective module names. qmake gets these information from config.modulename.libraries, and config.modulename.exports created before in qtConfSetupLibraries.

Now the exciting moment comes finally: “Running configuration tests…”. But before running configuration tests, there are two things to do: create a log file c:\qtbuild\config.log to save the config test commands and results;check license,i.e., you will see the following prompt:

This is done in qtConfProcessEarlyChecks. It will check every entry under the earlyReport key of c:\Qt5.12.1\5.12.1\Src\qtbase\configure.json. A condition in one of the entries is “condition”: “!call.licenseCheck”. To evaluate this condition, qtConfFunc_licenseCheck in  c:\Qt5.12.1\5.12.1\Src\qtbase\configure.pri is called, where you need to select a commercial license or a open source license.

Next, qmake will check every feature in the features key of c:\Qt5.12.1\5.12.1\Src\qtbase\configure.json.

The first feature is kind of special and needs attention:

"prepare": {
    "condition": "features.gcc-sysroot || true",
    "output": [ "prepareSpec", "prepareOptions", "preparePaths", "reloadSpec" ]

Look at its output, qmake will call qtConfOutput_prepareSpec, qtConfOutput_prepareOptions, qtConfOutput_preparePaths, qtConfOutput_reloadSpec, which actually write something to qt.conf such as “TargetSpec=win32-g++”. Note that configure.bat created the qt.conf and put it parralel to qmake.exe. qt.conf contains some settings which form the values of so called qmake properties. The QMAKE_XSPEC property is determined by TargetSpec and used to load spec file from proper directory(c:\Qt5.12.1\5.12.1\Src\qtbase\mkspecs\win32-g++\qmake.conf). qmake.conf provides more qmake variables such as QMAKE_CXX,QMAKE_CC, QMAKE_CXXFLAGS, QMAKE_CFLAGS, QMAKE_LINK, etc.

Some modules have a config.test sub-dir(QMAKE_CONFIG_TESTS_DIR) in their source directory such as c:/Qt5.12.1/5.12.1/Src/qtbase/config.tests/, c:/Qt5.12.1/5.12.1/Src/qt3d/config.tests/, c:/Qt5.12.1/5.12.1/Src/qt3d/src/render/config.tests/. A module may have many features to test. These features are organized by their types. Each feature type has a sub-dir in the config.test directory. For example, the x86_simd feature type of qtbase module corresponds to c:\Qt5.12.1\5.12.1\Src\qtbase\config.tests\x86_simd\(i.e., test_dir). These directories usually contain a .cpp file and a .pro file. In actual configure testing, qmake is executed against the .pro file to generate a Makefile(in c:\qtbuild\config.tests\x86_simd\,i.e, test_out_dir), then Make command is executed with some flags specific to the feature to be test, to generate an .exe program(also in c:\qtbuild\config.tests\x86_simd\). If the exectable can be made successfully, the feature can be enabled.

The config tests are done in qtConfProcessFeatures. qmake will check every feature listed under the “features” key of configure.json. We take the sse2 feature for an example. If you add a subkey “available” under features/sse2, the feature won’t be tested because you tell qmake it is always avaliable. Some features have a emitIf subkey, i.e., the feature incredibuild_xge has “emitIf”: “var.QMAKE_HOST.os == ‘Windows'”.

"incredibuild_xge": {
    "label": "IncrediBuild",
    "emitIf": "var.QMAKE_HOST.os == 'Windows'",
    "condition": "tests.incredibuild_xge",
    "output": [ "publicConfig" ]

in which case, the condition specified by emitIf is evaluated. If the condition is not satisfied, the feature is not avaible.

Then qmake check if the feature is enabled. A feature is enabled if it has a “enable” subkey and the value of the subkey is evaluated to be true. For example,

"rpath_dir": {
    "label": "RPATH directory",
    "enable": "input.rpaths != ''",
    "autoDetect": false,
    "output": [ { "type": "varAppend", "name": "EXTRA_RPATHS", "value": "input.rpaths" } ]

The rpath_dir is enabled if you input a non-empty rpaths. Of course, you can enable the feature explictly on the command line, regardless of the enable subkey.

Similarly, you can disable a feature on command line or by adding a subkey “disable” under the feature.

Some features have a “autoDetect” subkey. If you do not enable the feature and the autoDetect condition is evaluated to be false, the feature is not available.

For those features that are enabled or whose autoDetect condition are satisfied, we are still sure if they are available.  They are available only the conditions listed in the condition subkey of the feature are all satisfied. An atomic condition is evaluated in qtConfEvaluateSingleExpression. For the sse2 feature,

"sse2": {
    "label": "SSE2",
    "condition": "(arch.i386 || arch.x86_64) && tests.sse2",
    "output": [
        { "type": "define", "name": "QT_COMPILER_SUPPORTS_SSE2", "value": 1 }

There are 3 atomic condition. The feature is avalible only on i386/x86_64 archectures and past the sse2 test. Let’s see how to check the feature can pass the sse2 test.  To tell qmake to test your feature, you must list the feature under the “tests” subkey in configure.json. sse2 is indeed listed there:

"sse2": {
    "label": "SSE2 instructions",
    "type": "x86Simd"

qmake calls qtRunSingleTest to run the test. qtRunSingleTest looks for “type” in the sse2 subkey and gets “x86Simd“. If you have defined qtConfTestPrepare_x86Simd, it will call that function to do pre-test. If the pre-test fails, the whole test fails too. If pre-test passes, it will call qtConfTest_x86Simd(tests.sse2) defined in c:\Qt5.12.1\5.12.1\Src\qtbase\configure.pri to do the main test. qtConfTest_x86Simd will prepare the test directory(c:\Qt5.12.1\5.12.1\Src\qtbase\config.tests\x86_simd\) and proper compiler flags(SIMD=sse2) to call qtConfTest_compile in c:\Qt5.12.1\5.12.1\Src\qtbase\mkspecs\features\qt_configure.prf to execute the actual compiling commands(as we’ve talked). If the compiling is successful, you will see the generated .exe: c:\qtbuild\config.tests\x86_simd\x86_simd.exe. Accordinf to the test result, config.qtbase.features.sse2.available is set.

qmake outputs the information about whether a feature is available to several files. You may notice about the “output” subkey under features/sse2/. For sse2, there are three entries in the value of output: privateConfig, privateFeature, and { “type”: “define”, “name”: “QT_COMPILER_SUPPORTS_SSE2″, “value”: 1 } . qmake will call qtConfOutput_privateConfig, qtConfOutput_privateFeature, and qtConfOutput_define. These functions will create variables like config.qtbase.output.privatePro.append.CONFIG += sse2 to save those available features. The call stack is:

qtConfProcessOneOutput(sse2, 0)

qtConfOutput_privateConfig(config.qtbase.features.sse2.output.0, true)

qtConfOutputConfigVar(config.qtbase.features.sse2.output.0 , true, "privatePro", "CONFIG", false)

qtConfOutputVar(append, "privatePro", "CONFIG", sse2)


If there is a module, the last function would be

qtConfExtendVar(“privatePro”, “QT.qtbase.CONFIG”, sse2), which produces the following variable:

config.qtbase.output.privatePro.assign.QT.qtbase.CONFIG += sse2

When all output.* variables of a module are ready, qtConfProcessOutput will write them to files on disk. Specifically, for any module/submodule(corresponding to a subdir in src/ such as c:\Qt5.12.1\5.12.1\Src\qtbase\src\corelib\), qmake will create a corresponding directory(shadow directory) like C:\qtbuild\qtbase\src\corelib\, and generates 3 files there:qtcore-config.pri, qtcore-config.h, qtcore-config_p.h. You can customize the file names in the “files” subkey in configure.json. Now, config.qtbase.output.privatePro.append.CONFIG  will be saved to C:\qtbuild\qtbase\mkspecs\qmodule.pri as a line “CONFIG += sse2 …” In short, if you want to output CONFIG+=… to the privatePro file, you need to add an entry privateConfig in the output subkey. If you want to output QT.global_private.enabled_features=… or QT.global_private.disabled_features=… in the privatePro file, you need to add an entry privateFeature in the output subkey. If you want to output #define in publicHeader, you need to add an entry like { “type”: “define”, “name”: “QT_COMPILER_SUPPORTS_SSE2″, “value”: 1 } in the output subkey.

Note that the config test is done for all modules in allModuleConfigs except builtins, i.e., the modules/sub-modules that have configure.json and there are other keys in configure.json apart from subconfigs. So the module qt3d won’t be tested because its configure.json contains nothing but subconfigs. qt3d’s submodules qt3d_core, qt3d_render, and qt3d_geometryloaders will undergo config test because their configure.json have other keys than subconfigs. So you will find the qtxxxx-config.h, qtxxxx-config_p.h, qtxxxx-config.pri in c:\qtbuild\qt3d\src\core, c:\qtbuild\qt3d\src\plugins\geometryloaders, and c:\qtbuild\qt3d\src\render, but you cannot find them in c:\qtbuild\qt3d\. You may ask why the module qt has not such files generated. Well that is because the configure.json in c:\Qt5.12.1\5.12.1\Src does not contain the features key. qmake won’t produce these configure files for those modules without a features key, i.e., without features to test. You may also ask why I cannot find these configure result files in c:\qtbuild\qtbase\? That is because the configure files for the module qtbase are put in C:\qtbuild\qtbase\src\corelib\global\(qconfig.cpp, qconfig.h, qconfig_p.h, c:\qtbuild\qtbase\mkspecs\qmodule.pri, c:\qtbuild\qtbase\mkspecs\qconfig.pri), as specified in the files key of C:\Qt5.12.1\5.12.1\Src\qtbase\configure.json. Only when a module has some features to test but does not specify the outcome file names does qmake put the generated config results to the shadow directory of the module. For example, the submodule of qtbase: qtbase_gui, qtbase_sql, qtbase_xml, etc all get their config result put in their respective shadowed direcorties: C:\qtbuild\qtbase\src\gui\, C:\qtbuild\qtbase\src\sql\, C:\qtbuild\qtbase\src\xml\. etc.

A module/sub-module that has some features to test must has a files subkey or a module subkey in its configure.json. If it provides a module subkey, the configure result files are named after:

  • qt$${module}-config.pri
  • qt$${module}-config.h
  • qt$${module}-config_p.h

,and put in the module’s shadow directory. For example, “core” is the module value of submodule qtbase_corelib of qtbase, the qtcore-config.pri file contains the content from config.qtbase_corelib.output.privatePro and config.qtbase_corelib.output.publicPro. While the content of config.qtbase_corelib.output.privatePro is got from config.qtbase_corelib.output.privatePro .assign(or remove, or append).CONFIG, etc. For example, if there is  config.qtbase_corelib.output.privatePro. append.CONFIG=pcre2, you will see a line CONFIG += pcre2 in qtcore-config.pri. The qtcore-config.h and qtcore-config_p.h contain some #define statements for C language. If there is config.qtbase_corelib.output.publicHeader.QT_FEATURE_properties=1, you will see the following #define statement in qtcore-config.h:

#define QT_FEATURE_properties 1

Apart from config.qtbase_corelib.output variables, the content of the configure result files can also be filled by qtConfOutputPostProcess_xxxx functions. For global modules(those who do not have the module subkey in configure.json) like qtbase, the post-process functions are in C:\Qt5.12.1\5.12.1\Src\qtbase\configure.pri. Other modules may provide their post processing functions of the form qtConfOutputPostProcess_ $$module_privatePro in their respective project include files, although there is no module to do so till now.

Now config test-the most time-consuming part of qt configure is accomplished. Qmake will display the result on screen and write the result to c:\qtbuild\config.summary as well. The config summary (if no configure error) comprises of 3 parts in order: report, notes, and warnings. The notes,  warnings,and erros were produced after checking features of each module, in qtConfCreateReport. it checks every entry under the report key of configure.json checking if its condition is satisifed and if so, putting the message to QT_CONFIGURE_NOTES, QT_CONFIGURE_WARNINGS, or QT_CONFIGURE_ERRORS, according to the report type. The summaries were created by qtConfCreateSummary similarly, but to check the summary key of configure.json, and put in QT_CONFIGURE_REPORTS. So the summaries are displayed before the reports.

Let’s look carefully at the configure summary of the qtbase module.

Build type: win32-g++ (x86_64, CPU features: cx16 mmx sse sse2 sse3)
Compiler: gcc 7.3.0
Configuration: sse2 aesni sse3 ssse3 sse4_1 sse4_2 avx ....

The above lines are output in qtConfReport_buildTypeAndConfig in C:\Qt5.12.1\5.12.1\Src\qtbase\configure.pri triggered by

"summary": [
        "type": "buildTypeAndConfig"

The build type is the value of  QMAKE_SPEC. The architecture information was got during config test. See the architecture subkey of tests key of configure.json:

"architecture": {
    "label": "target architecture",
    "type": "architecture",
    "test": "arch",
    "log": "arch"

The qtConfTest_architecture function defined in C:\Qt5.12.1\5.12.1\Src\qtbase\configure.pri will build an .exe (C:\qtbuild\config.tests\arch\arch.exe) and parse the content of the .exe to get its architecture, sub-architecture, and build-API. Speciafically, the architecute is indicated by the “==Qt=magic=Qt== Architecture:xxxx ” string in the .exe file. The sub-architecture is indicated by the “==Qt=magic=Qt== Sub-architecture:xxxx” string in the .exe. The build API is indicated by the “==Qt=magic=Qt== Build-ABI:xxxx” in the .exe file. What a hacker method!

The compiler informatin(gcc 7.3.0) is obtained from the values of QMAKE_GCC_MAJOR_VERSION, QMAKE_GCC_MINOR_VERSION, QMAKE_GCC_PATCH_VERSION.

The configuration informatin is got from config.qtbase.output.privatePro.append.CONFIG and config.qtbase.output.publicPro.append.QT_CONFIG. We’ve known that these variables store the available features during config tests.

Now comes the next part of the configure summary of qtbase.

Build options:
  Mode ................................... debug and release; default link: debug; optimized tools
  Optimize debug build ................... yes
  Optimize release build for size ........ no
  Building shared libraries .............. yes
  Using C standard ....................... C11
  Using C++ standard ..................... C++1z


This is triggered by the “Build options” section of the “summary” key of configure.json.

"summary": [
         "type": "buildTypeAndConfig"
         "section": "Build options",
         "entries": [
                 "message": "Mode",
                 "type": "buildMode"
                 "type": "feature",
                 "args": "optimize_debug",
                 "condition": "!config.msvc && !config.clang && (features.debug || features.debug_and_release)"

The build mode information is output by qtConfReport_buildMode.

After outputting the configure reports, the control flow goes from qt_configure.prf to and then exits But the main job of qmake is just beginning: creating the Makefile with SUBDIR template. After the qmake and the configure.bat script end, you can see the Makefile generated in c:\qtbuild\. Of couse, this is only the Makefile for the top Qt directory. You can execute “mingw32-make” in C:\qtbuild\, it will call qmake to generate Makefiles for every sub-directories based on the .pro files in respective source directories, then call “Mingw32-make -f Makefile” for all directories to build the libs and executables for all Qt components.

From my analysis, you can see the whole qmake system is very complex, confusing, and does few things. From my experience, all software companies that have their own programming languages aim not to speed up their development and boost productivity, but set up technical barrier for people outside their team. This is even the case inside their team if the communication is not fluent between team members. The information communication flow inside team is mainly controlled by managers, which may also become the bottle-neck of the whole software development activities.


Posted in

Leave a Reply