Here’s what I searched for, before figuring out how to get SUBDIRS
dependencies working using qmake
:
“qmake export compiler flags”
“qmake export defines”
“qmake export includepath”
“qmake build dependent projects”
“qt creator automatically build dependent libraries”
Problem 1: Detecting Dependency Changes
Qt Creator has some weird behaviors. As in, it doesn’t properly do dependent library builds all of the time, particularly when you’re using a SUBDIRS
project template. I noticed that I would always have to right-click a project and “Run qmake” before running “Build X Project”, which was annoying. And I noticed that libraries wouldn’t automatically build and link to their consuming executables. It didn’t make sense, as I had used the “Add Library” option to add configuration blocks, like the following, to the executable project files that needed them:
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../SomeLibrary/release/ -lSomeLibrary else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../SomeLibrary/debug/ -lSomeLibrary else:unix: LIBS += -L$$OUT_PWD/../SomeLibrary/ -lSomeLibrary INCLUDEPATH += $$PWD/../SomeLibrary/source DEPENDPATH += $$PWD/../SomeLibrary/source win32:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../SomeLibrary/release/SomeLibrary.lib else:win32:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../SomeLibrary/debug/SomeLibrary.lib else:unix: PRE_TARGETDEPS += $$OUT_PWD/../SomeLibrary/libSomeLibrary.a
Once that block was in place, I figured all would be good. However, all that Qt Creator would do is tell me was that libSomeLibrary.a
wasn’t available, if I’d run a Clean on all of the projects, rather than just building and linking it for me. As in, make
would know the library file was missing (via the TARGET_PREDEPS
setting), but it would be too stupid to just build that target. And it wasn’t clear that any changes to SomeLibrary
were being built and linked into the consuming executable, since SomeLibrary
wasn’t getting autobuilt as a dependency. It seemed like old SomeLibrary
code was getting linked instead, and only the executable’s changed code was getting recompiled before the link. This was a dependency nightmare in the making.
On all of these particular points, the qmake manual does not clarify that SUBDIRS
projects are essentially a completely different beast, and require different rules to set up.
Not so useful.
The way to properly define dependencies in a SUBDIRS
project is to use a master Project file that looks something like:
cache() TEMPLATE = subdirs SUBDIRS += \ ConsoleTest \ # an "app" project SomeLibrary \ # a "lib" project SomeExternalLibrary # a "lib" project # ConsoleTest.subdir is implicitly set to "ConsoleTest". SomeLibrary.subdir = SomeLibrary SomeExternalLibrary.subdir = external/SomeExternalLibrary # Here's where you build up the hierarchical relationship between components. SomeLibrary.depends = SomeExternalLibrary ConsoleTest.depends = SomeLibrary
This is all you need to do. The next time the project file is parsed and qmake
is run to generate the platform-specific Makefile
s, it will generate the dependencies properly. No more weird builds and dependency problems. As much as I appreciate Qt’s awesome software, this is something they really need to clarify how to get working in their manual.
Problem 2: Exporting DEFINES
and INCLUDEPATH
In addition to the above, the method to set up the various projects to export the qmake
DEFINES
and INCLUDEPATH
flags used to build them was not clear from the documentation.
What you want in a hierarchical project configuration, ideally, is that the whole project rebuilds if any of the library sources, includes, included folders, or compiler DEFINES
changes. You want any library consumers to use the exact same settings used to build the libraries, and you want this to happen if the setting changes in a single, authoritative place (preferably the library build configuration). The only reference I could find to “library dependencies” in the qmake manual is not clear as to its mechanism for propagating this information, particularly the INCLUDEPATH
and DEFINES
settings.
After reading a handful of other references (1), (2), I finally figured out how to do this. It turns out that qmake has a lot of power to it, but that power is buried in bad documentation. (Why does no one invest in documentation? It’s an excellent opportunity to generate goodwill.)
The trick is twofold:
For each library project, you need to split the declaration of DEFINES
and INCLUDEPATH
into a Defines.pri
file at the same level as the .pro
Project file, for example:
/SomeLibrary /SomeLibrary/SomeLibrary.pro /SomeLibrary/Defines.pri
In Defines.pri
, add the DEFINES
and INCLUDEPATH
settings:
DEFINES += SOME_COMPILER_SETTING message(Including $$_FILE_ from $$IN_PWD) INCLUDEPATH += $$IN_PWD/source
In SomeLibrary.pro
, add
include(Defines.pri)
In any other project, include the DEFINES
and INCLUDEPATH
settings using a similar include
:
include(/SomeLibrary/Defines.pri)
You should then see something like "Including /SomeLibrary/Defines.pri from /SomeLibrary"
show up in the General Messages window in Qt Creator, when the project files are reparsed, meaning that the dependent project pulled in these settings properly. In this way, any changes made to the Defines.pri
will get propagated to the consuming projects.
So that’s it. Good luck.
Update
After some experimentation, you still need to “Add Library” the static library configuration blocks to the .pro
project file of any consuming executables, for some reason when I tried moving these configuration blocks from the executable .pro
files to the library .pri
files, qmake
started getting stuck in an infinite loop.
Omitting these configuration blocks will also cause the executables not to link. I don’t think I’m going to investigate this more closely, since solving the includeable DEFINES
/INCLUDEPATH
problem took care of a bigger build consistency issue. (But maybe I’ll come back to it.)
I just saw your post where you explained of automatic linking of library’s, I’ve made a project where this is automated. It still lacks some documentation but i will do this when I have the time, till then you could check it out?
github.com/MrCrazyID/QtCreator_SubDirExample
There is a way to solve your library issue using a similar method for the defines and includepaths. What I did was use add_library and then take the resulting generated code and massage them to do it all. I don’t have much for defines, but it will work as well. At the top level of your project, create a .pri for each library such that it looks like this:
include(top_srcdir.pri)
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../arinc_io_adapter/release/ -larinc_io_adapter
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../arinc_io_adapter/debug/ -larinc_io_adapter
else:unix: LIBS += -L$$OUT_PWD/../arinc_io_adapter/ -larinc_io_adapter
INCLUDEPATH += $$aop_srcdir/core/arinc_io_adapter \
$$aop_srcdir/core/arinc_io_adapter/arinc_format
#DEPENDPATH += $$aop_srcdir/core/arinc_io_adapter \
# $$aop_srcdir/core/arinc_io_adapter/arinc_format
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arinc_io_adapter/release/libarinc_io_adapter.a
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arinc_io_adapter/debug/libarinc_io_adapter.aelse:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arinc_io_adapter/release/arinc_io_adapter.lib
else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arinc_io_adapter/debug/arinc_io_adapter.lib
else:unix: PRE_TARGETDEPS += $$OUT_PWD/../arinc_io_adapter/libarinc_io_adapter.a
Now, all you have to do is include this .pri wherever its needed and you will get the library definition as well as any associated includepaths