Updated my weird QMake post

This commit is contained in:
Chris Hodapp 2016-06-04 23:05:24 -04:00
parent 0a1db37273
commit b44b667dd3

View File

@ -1,24 +1,39 @@
--- ---
layout: post
title: QMake hackery: Dependencies & external preprocessing title: QMake hackery: Dependencies & external preprocessing
tags: Project, Technobabble tags: Project, Technobabble
status: publish date: November 13, 2011
type: post author: Chris Hodapp
published: true
--- ---
* TODO: Put the code here into a Gist? * TODO: Put the code here into a Gist?
[Qt Creator](http://qt-project.org/wiki/Category:Tools::QtCreator) is a favorite IDE of mine for when I have to deal with miserably large C++ projects. At my job I ported a build in Visual Studio of one such large project over to Qt Creator so that builds and development could be done on OS X and Linux, and in the process, learned a good deal about [QMake](http://doc.qt.nokia.com/latest/qmake-manual.html) and how to make it do some unexpected things. [Qt Creator](http://qt-project.org/wiki/Category:Tools::QtCreator) is
a favorite IDE of mine for when I have to deal with miserably large
C++ projects. At my job I ported a build in Visual Studio of one such
large project over to Qt Creator so that builds and development could
be done on OS X and Linux, and in the process, learned a good deal
about [QMake](http://doc.qt.nokia.com/latest/qmake-manual.html) and
how to make it do some unexpected things.
While I find Qt Creator to be a vastly cleaner, lighter IDE than Visual Studio, and find QMake to be a far more straightforward build system for the majority of things than Visual Studio's build system, some things the build needed were very tricky to set up in QMake. The two main shortcomings I ran into were: While I find Qt Creator to be a vastly cleaner, lighter IDE than
* Managing dependencies between projects, as building the application in question involved building 40-50 separate subprojects as libraries, many of which depended on each other. Visual Studio, and find QMake to be a far more straightforward build
* Having external build events, as the application also had to call an external tool (no, not **moc**, this is different) to generate some source files and headers from a series of templates. system for the majority of things than Visual Studio's build system,
some things the build needed were very tricky to set up in QMake. The
two main shortcomings I ran into were:
QMake, as it happens, has some commands that actually make the project files Turing-complete, albeit in a rather ugly way. The **eval** command is the main source of this, and I made heavy use of it. * Managing dependencies between projects, as building the application
in question involved building 40-50 separate subprojects as
libraries, many of which depended on each other.
* Having external build events, as the application also had to call an
external tool (no, not `moc`, this is different) to generate some
source files and headers from a series of templates.
QMake, as it happens, has some commands that actually make the project
files Turing-complete, albeit in a rather ugly way. The `eval`
command is the main source of this, and I made heavy use of it.
First is the dependency management system. It's a little large, but I'm including it inline here. First is the dependency management system. It's a little large, but I'm including it inline here.
{% highlight bash %} ```bash
# This file is meant to be included in from other project files, but it needs # This file is meant to be included in from other project files, but it needs
# a particular context: # a particular context:
# (1) Make sure that the variable TEMPLATE is set to: subdirs, lib, or app. # (1) Make sure that the variable TEMPLATE is set to: subdirs, lib, or app.
@ -132,19 +147,38 @@ contains(TEMPLATE, "app") | contains(TEMPLATE, "lib") {
contains(_valid, "false") { contains(_valid, "false") {
error("Don't recognize template type: " $${TEMPLATE}) error("Don't recognize template type: " $${TEMPLATE})
} }
{% endhighlight %} ```
It's been sanitized heavily to remove all sorts of details from the huge project it was taken from. Mostly, you need to add your dependent projects into the "Table of Project Locations" section, and perhaps make another file that set up the necessary variables mentioned at the top. Then set the **DEPENDS** variable to a list of project names, and then include this QMake file from all of your individual projects (it may be necessary to include it pretty close to the top of the file). It's been sanitized heavily to remove all sorts of details from the
huge project it was taken from. Mostly, you need to add your dependent
projects into the "Table of Project Locations" section, and perhaps
make another file that set up the necessary variables mentioned at the
top. Then set the `DEPENDS` variable to a list of project names, and
then include this QMake file from all of your individual projects (it
may be necessary to include it pretty close to the top of the file).
In general, in this large application, each sub-project had two project files: In general, in this large application, each sub-project had two
* One with **TEMPLATE = lib** (a few were **app** instead as well). This is the project file that is included in as a dependency from any project that has **TEMPLATE = subdirs**, and this project file makes use of the QMake monstrosity above to set up the include and library paths for any dependencies. project files:
* One with **TEMPLATE = subdirs**. The same QMake monstrosity is used here to include in the project files (of the sort in #1) of dependencies so that they are built in the first place, and permit you to build the sub-project standalone if needed.
...and both are needed if you want to be able to build sub-project independently and without making to take care of dependencies individually. * One with `TEMPLATE = lib` (a few were `app` instead as well). This
is the project file that is included in as a dependency from any
project that has `TEMPLATE = subdirs`, and this project file makes
use of the QMake monstrosity above to set up the include and library
paths for any dependencies.
* One with `TEMPLATE = subdirs`. The same QMake monstrosity is used
here to include in the project files (of the sort in #1) of
dependencies so that they are built in the first place, and permit
you to build the sub-project standalone if needed.
The next project down below sort of shows the use of that QMake monstrosity above, though in a semi-useless sanitized form. Its purpose is to show another system, but I'll explain that below it. ...and both are needed if you want to be able to build sub-project
independently and without making to take care of dependencies
individually.
{% highlight bash %} The next project down below sort of shows the use of that QMake
monstrosity above, though in a semi-useless sanitized form. Its
purpose is to show another system, but I'll explain that below it.
```bash
QT -= gui QT -= gui
QT -= core QT -= core
TEMPLATE = lib TEMPLATE = lib
@ -233,9 +267,15 @@ for(templatefile, GENERATOR_INPUTS) {
# Also add our header files. I doubt it's really necessary, but here it is. # Also add our header files. I doubt it's really necessary, but here it is.
HEADERS += $${HEADER} HEADERS += $${HEADER}
} }
{% endhighlight %} ```
This one uses a bit more black magic. The entire **GENERATOR_INPUTS** list is a set of files that are inputs to an external program that is called to generate some code, which then must be built with the rest of the project. This uses undocumented QMake features, and a couple kludges to generate some things dynamically (i.e. the filenames of the generated code) from a variable-length list. I highly recommend avoiding it. However, it does work. This one uses a bit more black magic. The entire `GENERATOR_INPUTS`
list is a set of files that are inputs to an external program that is
called to generate some code, which then must be built with the rest
of the project. This uses undocumented QMake features, and a couple
kludges to generate some things dynamically (i.e. the filenames of the
generated code) from a variable-length list. I highly recommend
avoiding it. However, it does work.
These two links proved indispensable in the creation of this: These two links proved indispensable in the creation of this: