How does qmake generate include path?

If you have read my post “Where does Qt find header files“, you have known that qmake will generate some include paths in the makefile to compile your project. For example, if you build the qt gui module in the qt src with the command:

cd c:\mybuild\qtbase\src\gui
..\..\bin\qmake c:\Qt\Qt5.12.1\5.12.1\Src\qtbase\src\gui\gui.pro

You will find the following include paths in the generated Makefile.Debug:

INCPATH       = -IC:\Qt\Qt5.12.1\5.12.1\Src\qtbase\src\gui -I. -IC:\Qt\Qt5.12.1\5.12.1\Src\qtbase\include -IC:\Qt\Qt5.12.1\5.12.1\Src\qtbase\include\QtGui -I..\..\include -I..\..\include\QtGui -IC:\Qt\Qt5.12.1\5.12.1\Src\qtbase\include\QtGui\5.12.1 -IC:\Qt\Qt5.12.1\5.12.1\Src\qtbase\include\QtGui\5.12.1\QtGui -I..\..\include\QtGui\5.12.1 -I..\..\include\QtGui\5.12.1\QtGui -Itmp -I.tracegen\debug -IC:\Qt\Qt5.12.1\5.12.1\Src\qtbase\include\QtCore\5.12.1 -IC:\Qt\Qt5.12.1\5.12.1\Src\qtbase\include\QtCore\5.12.1\QtCore -I..\..\include\QtCore\5.12.1 -I..\..\include\QtCore\5.12.1\QtCore -IC:\Qt\Qt5.12.1\5.12.1\Src\qtbase\include\QtCore -I..\..\include\QtCore -I.moc\debug -IC:\Qt\Qt5.12.1\5.12.1\Src\qtbase\src\3rdparty\libpng -IC:\Qt\Qt5.12.1\5.12.1\Src\qtbase\src\3rdparty\harfbuzz-ng\include -IC:\Qt\Qt5.12.1\5.12.1\Src\qtbase\mkspecs\win32-g++

I did not write any INCLUDEPATH  in the gui.pro, how does qmake add so many include paths in the makefile? Those programmers are good at creating technical barriers by hiding things. I will reveal what is under the hood for you.

All start from the qmake variable _QMAKE_CONF_. This is not a qmake variable set in some .pro/.prf/.pri file, but created in the C++ code of qmake:

bool QMakeEvaluator::loadSpec()
{
..........
valuesRef(ProKey("_QMAKE_CONF_")) << ProString(m_conffile);
..........
}

qmake finds the configure file(.qmake.conf=>m_conffile) in the directory the .pro file resides(c:\Qt\Qt5.12.1\5.12.1\Src\qtbase\src\gui), its parent directory, its grand-parent directory, …, till the root directory(C:\), in order. In this case, it finds the .qmake.conf as C:\Qt\Qt5.12.1\5.12.1\Src\qtbase\.qmake.conf.

During parsing c:\Qt\Qt5.12.1\5.12.1\Src\qtbase\src\gui\gui.pro, qmake meets the following line:

load(qt_module)

qt_module.prf will load qt_build_paths.prf which has the following line:

MODULE_BASE_INDIR = $$dirname(_QMAKE_CONF_)

Now, the qmake variable MODULE_BASE_INDIR is set to c:\Qt\Qt5.12.1\5.12.1\Src\qtbase. Then qt_module.prf loads qt_module_headers.prf which creates the variable MODULE_INCLUDES whose content is: MODULE_INCLUDES:$$QT_MODULE_INCLUDE_BASE $$QT_MODULE_INCLUDE_BASE/QtGui

qt_module.prf continues to load qt_module_pris.prf which creates qt_lib_gui.pri and qt_lib_gui_private.pri in C:\mybuild\qtbase\mkspecs\modules-inst, and qt_lib_gui.pri(so called forwarding module .pri file) in C:\mybuild\qtbase\mkspecs\modules. Notice a line when creating the content of the forwarding module .pri file qt_lib_gui.pri:

QT_MODULE_INCLUDE_BASE = $$val_escape(MODULE_BASE_INCDIR)/include

So the qt_lib_gui.pri will have the following line in it:

QT_MODULE_INCLUDE_BASE =C:/Qt/Qt5.12.1/5.12.1/Src/qtbase/include

The forwarding  qt_lib_gui.pri has two lines to include  qt_lib_gui.pri and qt_lib_gui_private.pri in C:\mybuild\qtbase\mkspecs\modules-inst, respectively. One of the lines in C:\mybuild\qtbase\mkspecs\modules-inst\qt_lib_gui.pri is:

QT.gui.includes = $$QT_MODULE_INCLUDE_BASE $$QT_MODULE_INCLUDE_BASE/QtGui

During the build pass, the files in C:\mybuild\qtbase\mkspecs\modules and C:\mybuild\qtbase\mkspecs\modules-inst will be read so QT.gui.includes will get the correct value:

C:/Qt/Qt5.12.1/5.12.1/Src/qtbase/include C:/Qt/Qt5.12.1/5.12.1/Src/qtbase/include/QtGui C:/mybuild/qtbase/include C:/mybuild/qtbase/include/QtGui

The load order of .prf files during the build pass is: C:\Qt\Qt5.12.1\5.12.1\Src\qtbase\mkspecs\win32-g++\qmake.conf –> C:\Qt\Qt5.12.1\5.12.1\Src\qtbase\mkspecs\features\qt_config.prf which loads all .pri in C:\mybuild\qtbase\mkspecs\modules. You can get to know this in my post “what .prf files are automatically loaded by qmake?

Back to qt_module.prf, it will append what is in QT.gui.includes and QT.gui_private .includes, and a temporary directory tmp to INCLUDEPATH. The value of INCLUDEPATH is now:

C:/Qt/Qt5.12.1/5.12.1/Src/qtbase/include C:/Qt/Qt5.12.1/5.12.1/Src/qtbase/include/QtGui C:/mybuild/qtbase/include C:/mybuild/qtbase/include/QtGui C:/Qt/Qt5.12.1/5.12.1/Src/qtbase/include/QtGui/5.12.1 C:/Qt/Qt5.12.1/5.12.1/Src/qtbase/include/QtGui/5.12.1/QtGui C:/mybuild/qtbase/include/QtGui/5.12.1 C:/mybuild/qtbase/include/QtGui/5.12.1/QtGui tmp

It is now similar to that in the Makefile. As qmake loads CONFIG features, more include paths are appended to  INCLUDEPATH . For example, when loading CONFIG feature moc.prf, .moc\debug is added to INCLUDEPATH.

Let’s check how C:\Qt\Qt5.12.1\5.12.1\Src\qtbase\src\3rdparty\libpng is slipped into the include paths. You should run qmake against C:\Qt\Qt5.12.1\5.12.1\Src\qtbase\src\3rdparty\libpng\libpng.pro before running qmake against c:\Qt\Qt5.12.1\5.12.1\Src\qtbase\src\gui\gui.pro. There is a line in libpng.pro:

load(qt_helper_lib)

qt_helper_lib will create qt_ext_libpng.pri in C:\mybuild\qtbase\mkspecs\modules\. We can see how it prepares the content of qt_ext_libpng.pri:

QMAKE_INCDIR_LIBPNG = $$val_escape(MODULE_INCLUDEPATH)

As MODULE_INCLUDEPATH is set to the current directory(C:\Qt\Qt5.12.1\5.12.1\Src\qtbase\src\3rdparty\libpng\) in libpng.pro, the generated qt_ext_libpng.pri contains the following line:

QMAKE_INCDIR_LIBPNG = C:/Qt/Qt5.12.1/5.12.1/Src/qtbase/src/3rdparty/libpng

qt_ext_libpng.pri will be loaded when running qmake against c:\Qt\Qt5.12.1\5.12.1\Src\qtbase\src\gui\gui.pro as already talked about. gui.pro loads the image.pri as follows:

include(image/image.pri)

image.pri will add libpng to QMAKE_USE_PRIVATE.

During loading the CONFIG(qmake_use) feature file(qmake_use.prf) for gui.pro, it will append the content of QMAKE_INCDIR_LIBPNG to INCLUDEPATH:

INCLUDEPATH += $$eval(QMAKE_INCDIR_$${nu})

So, you will see C:\Qt\Qt5.12.1\5.12.1\Src\qtbase\src\3rdparty\libpng in INCLUDEPATH. The occurrence of C:\Qt\Qt5.12.1\5.12.1\Src\qtbase\src\3rdparty\harfbuzz-ng\include in INCLUDEPATH is similar.

Now let me show you how C:\Qt\Qt5.12.1\5.12.1\Src\qtbase\include\QtCore\xxxx get into INCLUDEPATH. Near the beginning of gui.pro, there is a line:

QT = core-private

Qt contains the modules the gui module depends on. Although Qt contains only core-private, core-private depends on core, so gui depends on both core-private and core. You can get to know this by looking at C:\mybuild\qtbase\mkspecs\modules-inst\qt_lib_core_private.pri which was generated when qmaking the core module.

QT.core_private.depends = core

qt_lib_core_private.pri also contains:

QT.core_private.includes = $$QT_MODULE_INCLUDE_BASE/QtCore/5.12.1 $$QT_MODULE_INCLUDE_BASE/QtCore/5.12.1/QtCore

qt_lib_core.pri contains:

QT.core.includes = $$QT_MODULE_INCLUDE_BASE $$QT_MODULE_INCLUDE_BASE/QtCore

During the stage of loading CONFIG(qt) feature qt.prf for gui.pro, QT.core.includes/QT.core_private.includes will be read into MODULE_INCLUDES which is appended to INCLUDEPATH.

So far we’ve found two methods to include a module/lib – by adding the lib name to  QMAKE_USE_PRIVATE/QMAKE_USE, or by adding the module name to QT/QT_PRIVATE.

Things are not over yet. To get it harder to find all include paths, they slip extra include paths in C++ code of qmake:

MakefileGenerator::init()
{
..........................
incs.prepend(qmake_getpwd());
..........................
 incs.append(project->specDir());
.....................
}

Now the include paths include the directory you run qmake(.) and the spec directory C:/Qt/Qt5.12.1/5.12.1/Src/qtbase/mkspecs/win32-g++

 

 

If you like my content, please consider buying me a coffee. Buy me a coffeeBuy me a coffee Thank you for your support!
Posted in

Leave a Reply