When reading the documentation for qmake
one finds something called QMAKE_EXTRA_COMPILERS
. The documentation is clear, but not really an everyday use-case. So I thought I would try to explain QMAKE_EXTRA_COMPILERS
with a “real” use-case from a project I’m currently working on for one of Combitechs many customers.
Background
In this project we generate code based on the content of a number of XML-files. Maybe one day I should write a bit about why we are generating code and how we wrote the code-generator as well. But for now the only thing we need to know is that there is a list of XML-files that we use as input to the code-generator and out comes a number of header-files.
When I proposed the code-generator for the rest of the project I promised them that this should be simple and easy to use for the rest. I might have said something like:
Just a parameter with the XML-files in the pro-file
So integrating the code-generation into our qmake
-based build was of highest priority. And thanks to QMAKE_EXTRA_COMPILERS
it was fairly simple.
What does QMAKE_EXTRA_COMPILERS do
You can read the full documentation of QMAKE_EXTRA_COMPILERS at qt.io. But in short:
- you setup an executable that qmake will execute.
- you provide the executable with input argument. You do not have to do, but would be strange not to.
- you add, if needed the output or data to some other
qmake
-parameter.
The example on qt.io is based around moc
and how to setup your own moc
-solution. But in this example we are going to pass a list of XML-files in to this.
Setting it up for code-generation
The goal was to keep the implementation in the main project file as simple as possible.
CG_INPUT += \
fileA.xml \
fileB.xml
include(code-generator.pri)
The above code will just create a parameter call CG_INPUT
that, in this case, holds fileA.xml and fileB.xml. The next thing we do is to load code-generator.pri
.
cg.name = "Internal code generator"
cg.input = CG_INPUT
cg.output = $$OUT_PWD/generated/${QMAKE_FILE_BASE}$${first(QMAKE_EXT_H)}
cg.variable_out = HEADERS
cg.commands = \
$$OUT_PWD/../code-generator/bin/cg -s -o $$OUT_PWD/generated -r ${QMAKE_FILE_IN}
QMAKE_EXTRA_COMPILERS += cg
DISTFILES += CG_INPUT
Let’s look what the code above does. cg.name
just sets a name on the extra compiler we are adding. cg.input = CG_INPUT
tells the extra compiler what its input are, in this case our XML-files (fileA.xml and fileB.xml).
cg.output
list the output files. This is where it starts to get a bit strange, to be honest. QMAKE_FILE_BASE
is one of de “undocumented” things in qmake
. What it will do is actually take the filename of, each and every, file from cg.input
and remove the file-ending (.xml
). After that we add the header-file ending. So our outputs, in this example, will be fileA.h and fileB.h.
cg.variable_out
is very useful. You use that to tell which parameter to append the output files to. In this example we append them to the HEADERS
-list. This will make sure qmake
knows about them.
cg.command
is the command you would like to execute. I’ve used ${QMAKE_FILE_IN}
as input to our code generator. ${QMAKE_FILE_IN}
will be fileA.xml resp. fileB.xml. Because we have two XML-files this application will be called twice.
The last, and most important thing, is to append cg
to the list of extra compilers. This is done by adding QMAKE_EXTRA_COMPILERS += cg
. The name cg
is short for code-generation but this variable could have any name, not just cg
.
Running it in QtCreator/qmake
This setup makes the use and integration of a proprietary code-generator really simple in QtCreator and qmake. It requires a really small amount of code in the project files to pull this simple hack off.
One thing that took a while to understand is that qmake will execute the cg.command
once for every file in CG_INPUT
, but only if the output header-file is used somewhere in the code.
Offcourse it holds dependencies “back” to the XML-file. If the XML-file content is change the code-generator will be executed on next compilation.