QMake-top-level-srcdir-and-builddir: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
 
mNo edit summary
 
(6 intermediate revisions by 4 users not shown)
Line 1: Line 1:
=QMake top level srcdir and builddir=
= 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:


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:
<code>
INCLUDEPATH ''= $$top_srcdir/include
</code>


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


or
or


QMake provides to out-of-the-box variables <span class="caps">PWD</span> and <span class="caps">OUT</span>_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.
<code>
include($$top_srcdir/path/to/config.pri)
</code>
 
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.


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:


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 ==


==Qt 4 based solution==
=== the .qmake.cache file ===


===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 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 <span class="caps">QMAKE</span>_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:


The second feature is the <span class="caps">QMAKE</span>_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:
<code>
Project version $$VERSION created with Qt $$QT_VERSION
</code>


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


===<span class="caps">SUBDIRS</span> with .pro files===
<code>
Project version 1.2.3 created with Qt 4.8.2
</code>
 
=== 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:
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:
<code>
TEMPLATE =subdirs
SUBDIRS = foo.pro bar.pro subdir
</code>


With '''foo.pro''' and '''bar.pro''' being in the same folder as the .pro file above, and subdir containing a '''subdir.pro''' as usual.
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==
== Solution ==


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


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


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


'''\.qmake.cache.in'''<br />
'''make.cache.in'''
<code>
top_srcdir=$$PWD
top_builddir=$$OUT_PWD
</code>


Basically we create a .qmake.cache file in the top-level build dir, that contains the variables top_srcdir and top_builddir generated from <span class="caps">PWD</span> and <span class="caps">OUT</span>_PWD (which point to the right dirs when expanded at the top-level).
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 <span class="caps">QMAKE</span>_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 <span class="caps">PWD</span> and <span class="caps">PWD</span>_OUT have the correct values.
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! <span class="smiley">:)</span>
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 based solution ==


Qt 5 makes the problem a non-challenge.
Qt 5 makes the problem a non-challenge.


Create a .qmake.conf file in your top-level source dir which contains these two lines:
Create a .qmake.conf file in your top-level source dir which contains these two lines:
<code>
top_srcdir=$$PWD
top_builddir=$$shadowed($$PWD)
</code>


Done.
Done.


===Categories:===
= See also =
* [[Undocumented QMake]]


* [[:Category:Developing-with-Qt|Developing with Qt]]
[[Category:Developing with Qt]]
** [[:Category:Developing-with-Qt::qmake|qmake]]
* [[:Category:HowTo|HowTo]]
* [[:Category:snippets|snippets]]

Latest revision as of 15:21, 28 August 2015

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:

INCLUDEPATH ''= $$top_srcdir/include

or

LIBS''= -L$$top_builddir/lib -lmylib

or

include($$top_srcdir/path/to/config.pri)

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:

Project version $$VERSION created with Qt $$QT_VERSION

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

Project version 1.2.3 created with Qt 4.8.2

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:

TEMPLATE =subdirs
SUBDIRS = foo.pro bar.pro subdir

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

TEMPLATE = subdirs
SUBDIRS = initvars.pro subdir1 subdir2 #subdir1 and subdir2 are your project subdirs

initvars.pro

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

make.cache.in

top_srcdir=$$PWD
top_builddir=$$OUT_PWD

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:

top_srcdir=$$PWD
top_builddir=$$shadowed($$PWD)

Done.

See also