Silmor . de
Site Links:
Impressum / Publisher

SUBDIRS for Fun and Profit

QMake insists on creating just one target per .pro file. These target types (TEMPLATE=...) are possible:

Sometimes there are situations where, even after refactoring the sources, you need to create several targets in the same directory. Step one is to create separate (differently named) .pro files for each target. The next step is to tie them together, so you do not need to remember to build each one separately.

An Example

Let's assume we developed the ultimate "simply showing the time in a window" application on earth, but we'd like to also make the clock widget available to other applications as a library.

Some sources for our example (all in the directory "myclock"):

// myclock/clock.h - declaring the widget interface
#include "QLabel"

class MyClockWidget:public QLabel
  private slots:
    void updateTime();
// myclock/clock.cpp - implementing the widget
#include "clock.h"
#include <QTimer>
#include <QDateTime>

  QTimer*tm=new QTimer(this);

void MyClockWidget::updateTime()

These two just define a very simple label that updates itself with the current time using a QTimer. The frequency is set to 10Hz to make sure updates appear smooth. The timer object will be automatically deleted together with its parent.

// myclock/mainprog.cpp - our simple program, just displaying the clock widget as window
#include "clock.h"
#include <QApplication>

int main(int argc,char**argv)
  QApplication app(argc,argv);
  MyClockWidget cw;;
  return app.exec();

This is the most simple widget application possible: it just shows the clock label as a window.

And finally the QMake .pro file for building the application:

# myclock/ - clock application build file
TARGET = clockapp
QT += widgets

SOURCES += clock.cpp mainprog.cpp
HEADERS += clock.h

This already compiles and works quite well. Try it: cd myclock; qmake && make.

If you do not like to type in or copy&paste the sources yourself - you can download them here.

In step one we add another .pro file to be able to build the library as well:

# myclock/ - clock widget library
TARGET = clockwidget
QT += widgets

SOURCES += clock.cpp
HEADERS += clock.h

Executing this on a modern Linux (without cleaning away the files left by the previous steps) we will be disappointed pretty quickly by the output of make:

bash$ qmake && make
/usr/bin/ld: moc_clock.o: relocation R_X86_64_PC32 against symbol `_ZN13MyClockWidget16staticMetaObjectE' 
 can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status
make: *** [] Error 1

This happens because different compiler parameters are needed to build an executable program versus a library. This can easily be solved by moving temporary files to a different place:

# myclock/
#... add this:
OBJECTS_DIR = .tmpapp
MOC_DIR = .tmpapp
RCC_DIR = .tmpapp
# myclock/
#... add this:
OBJECTS_DIR = .tmplib
MOC_DIR = .tmplib
RCC_DIR = .tmplib

(This example does not actually use ressource files (RCC_DIR), but it does not hurt to add this in anticipation of more changes...)

Note to purists and refactoring fans: yes, this could be refactored to a separate library and executable using the library, that each have separate directories. But keep in mind it is just an example that I wanted to keep simple. In reality sometimes practicalities and/or personalities take precedence - so please assume you've argued your case with your boss, were threatened with being fired, and finally gave in because you need the money...

To make things even more fun, we can add an example for how to use the clock widget library:

// myclock/example/example.cpp - our example program, just displaying the clock widget as window
#include "../clock.h"
#include <QApplication>

int main(int argc,char**argv)
  QApplication app(argc,argv);
  MyClockWidget cw;
  cw.setWindowTitle("Hello Example");;
  return app.exec();
# myclock/example/ - clock example build file
TARGET = clockexample
QT += widgets

SOURCES += example.cpp
LIBS += -L.. -lclockwidget

Note: on Linux you'll have to set export LD_LIBRARY_PATH=.. (or more appropriately the absolute path to myclock instead of "..") to ensure the library is found when you want to execute the example. On Windows you can either set the PATH variable or copy the DLL file to the same directory as the clockexample.exe.

Another note to purists and geniuses: yes, there are more clever and more standards compliant ways of ensuring an application finds its libraries. Simple. Remember?

A Main Project File

So far we have to call qmake and make separately for each target, but this is not necessary:

# myclock/
TEMPLATE = subdirs
SUBDIRS = app lib example

app.file =

lib.file =

example.depends = lib

The subdirs template of QMake makes qmake and make recurse into sub-projects without building an actual binary target itself. The name of this file was not chosen by accident: if qmake is called without parameters it tries to find a .pro file with the same name as the directory that qmake is running in, just with .pro appended - so with this name it is clear that we mean this to be the main project file of this directory.

For each "subdir" that is listed in the SUBDIRS variable we can add qualifying parameters that slightly alter the behavior of qmake.

The *.file parameter can be used to make qmake look in a different file than it expects. Normally qmake assumes that each "subdir" is an actual directory and it expects to find a .pro file with the same name as the directory in it. The app and lib targets are actually no real directories, so we use this parameter to tell qmake to remain in the same directory and use the other two .pro files instead.

The *.depends parameter can be used to tell qmake about dependencies between targets. In the case of example.depends we tell qmake to make sure the entire lib target is done before it starts with building the example, because the example actually needs the library. This is especially important for parallel builds (make -j ...) - otherwise the make process may try to link the example before the library is completely linked, which leads to relatively nasty linker errors.

Webmaster: webmaster AT silmor DOT de