QMake-top-level-srcdir-and-builddir

From Qt Wiki
Revision as of 14:31, 23 February 2015 by Maintenance script (talk | contribs)
Jump to navigation Jump to search

h1. QMake top level srcdir and builddir

When working on complex qmake-based projects with multiple subdirectories, having a pair of top_srcdir and top_builddir variables is very convenient, so you can for example say:

<br />INCLUDEPATH ''= $$top_srcdir/include<br />


or


<br />LIBS''= -L$$top_builddir/lib -lmylib<br />

or

<br />include($$top_srcdir/path/to/config.pri)<br />

QMake provides to out-of-the-box variables PWD and OUT_PWD, however those variables point to the current .pro/.pri file being processed and its build dir, meaning that they will change when recursing into subdirectories.

There are ways to workaround this limitation by defining top_srcdir and top_builddir yourself, or having them generated by a script before building. But all of those tricks require manual steps, which isn't nice for users who just want to open the project in Qt Creator and hit the build button. It doesn't play nice with CI systems and build farms either.

Fortunately, there's a way to make it work automatically, and even with support for in-source and out-of-source builds, using a couple of undocumented qmake features:

Qt 4 based solution

the .qmake.cache file

The first feature is the .qmake.cache file, which is technically a .pri file that gets automatically included in every .pro file your project contains, including those in subdirectories. It's the perfect place to define variables and settings that apply to all your .pro files, such as (you guessed it) top_srcdir and top_builddir.

the QMAKE_SUBSTITUTES variable

The second feature is the QMAKE_SUBSTITUTES qmake variable, which accepts files with a .in extension suffix in your srcdir and copies those files to the builddir by stripping the .in suffix, and most importantly by expanding (substituting) any qmake variable defined in the file. So for example if you had a file version.info.in containing:

<br />Project version $$VERSION created with Qt $$QT_VERSION<br />

this file would be created as version.info in your build dir and containing:

<br />Project version 1.2.3 created with Qt 4.8.2<br />

SUBDIRS with .pro files

Finally, the third undocumented feature we use is the possibility of executing arbitrary .pro files in a subdirs qmake project, rather than just recursing in subfolders. This allows you to do the following:

<br />TEMPLATE =subdirs<br />SUBDIRS = foo.pro bar.pro subdir<br />

With foo.pro and bar.pro being in the same folder as the .pro file above, and subdir containing a subdir.pro as usual.

Solution

With all this at hand, we can now put all the pieces together:

myproject.pro

<br />TEMPLATE = subdirs<br />SUBDIRS = initvars.pro subdir1 subdir2 #subdir1 and subdir2 are your project subdirs<br />

initvars.pro

<br />TEMPLATE=subdirs<br />SUBDIRS= # don't build anything, we're just generating the .qmake.cache file<br />QMAKE_SUBSTITUTES += .qmake.cache.in<br />

make.cache.in

<br />top_srcdir=$$PWD<br />top_builddir=$$OUT_PWD<br />

Basically we create a .qmake.cache file in the top-level build dir, that contains the variables top_srcdir and top_builddir generated from PWD and OUT_PWD (which point to the right dirs when expanded at the top-level).

You might wonder why we need a separate initvars.pro, instead of putting QMAKE_SUBSTITUTES directly in myproject.pro. The reason is that Qt Creator processes .pro files recursively by default (qmake -r), which results in all the subdirs being generated before the .qmake.cache file is generated, thus defeating the purpose of the solution. Using a separate initvars.pro ensures that .qmake.cache is generated before qmake starts processing the other subdirs. And obviously we need to put that file at the top-level too so PWD and PWD_OUT have the correct values.

So this solution works both for command-line-built projects and projects loaded in Qt Creator, for in-source and out-of-source builds (for in-source then top_srcdir == top_builddir). And it doesn't require any manual step. Success! :)

Qt 5 based solution

Qt 5 makes the problem a non-challenge.

Create a .qmake.conf file in your top-level source dir which contains these two lines:

<br />top_srcdir=$$PWD<br />top_builddir=$$shadowed($$PWD)<br />

Done.