1.6.  qmake, Project Files, and Makefile

[ fromfile: qmake.xml id: qmake ]

C++ applications are generally composed of many source files, header files, and external libraries. During the normal course of project development, source files and libraries get added, changed, or removed. To build an executable that reflects the current state of the project, such changes require all affected files to be compiled and the resulting object files to be properly linked. This changing-rebuilding process typically happens many times.

Keeping track of all the parts of such a project requires a mechanism that precisely specifies the input files involved, the tools needed to build, the intermediate targets and their dependencies, and the final executable target.

The most widely used utility for handling the job of building a project is make.[12] make reads the details of the project specifications and the instructions for the compiler from a Makefile, which resembles a shell script but contains (at a minimum)

The make command, by default, loads the file named Makefile from your current working directory and performs the specified build steps (compiling and linking).

The immediate benefit of using make is that it recompiles only the files that have changed or are affected by any changes, rather than blindly recompiling every source file every time. Figure 1.1 shows the steps involved in building a Qt application.

Figure 1.1.  (q)make build steps

(q)make build steps

With Qt, it is not necessary for the programmer to write Makefiles. Qt provides a tool called qmake to generate Makefiles for you. It is still necessary to somehow run make and understand its output. Most IDEs run make (or something similar) at the click of a button and either display or filter its output.

To create a Makefile, qmake requires a project file. A project file describes the project by listing all of the other files, and all the options and file locations, that are needed to build the project. It is much simpler than a Makefile and can easily be created by the user. It is much simpler than a Makefile and, although a programmer can easily create one, qmake can also generate a simple project file with the command qmake -project When this command is issued, qmake lists all source files (*.cpp) in the current working directory as SOURCES and all header files (*.h) in that directory as HEADERS. The resulting project file will be given the name that follows the optional -o switch. If that switch and its argument are not present, qmake uses the name of the current working directory to name the project file and also, eventually, the executable file.

After the project file has been created, the command qmake creates a Makefile based on the project file. The command make can then attempt to build an executable by following the instructions in the Makefile.[13] The name of the executable is specified by the TARGET variable. It defaults to the name of the project.

The following transcript shows how to use qmake to build the same little program that we discussed, compiled, and ran in Example 1.1. The files that are created after each step of the build process are italicized.

src/early-examples/example0> ls
fac.cpp
src/early-examples/example0> qmake -project
src/early-examples/example0> ls
example0.pro fac.cpp
src/early-examples/example0> cat example0.pro

TEMPLATE = app
TARGET =
DEPENDPATH += .
INCLUDEPATH += .

# Input
SOURCES += fac.cpp
src/early-examples/example0> qmake
src/early-examples/example0> ls
example0.pro fac.cpp Makefile
src/early-examples/example0> make
g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_GUI_LIB
  -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I.
  -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtCore
  -I/usr/include/qt4/QtGui -I/usr/include/qt4/QtGui -I/usr/include/qt4
  -I. -I. -I. -o fac.o fac.cpp
g++ -o example0 fac.o  -L/usr/lib -lQtGui -lQtCore -lpthread
src/early-examples/example0> ls
example0 example0.pro fac.cpp fac.o Makefile
src/early-examples/example0>

Notice that you can see the arguments passed to the compiler when you run make. If any errors are encountered, you will also see them. Now you can run this application.

src/early-examples/example0> ./example0
Factorial of: 10
The Factorial of 10 is: 3628800
src/early-examples/example0> ./example0
Factorial of: -3
No negative values, please!
Factorial of: 0
The Factorial of 0 is: 1
src/early-examples/example0>
 

Instead of running the compiler directly from the command-line, you should henceforth use qmake and make, which greatly simplify the build process – especially when a project involves multiple source and header files and libraries.

Section C.1 discusses the make command and Makefiles in a bit more detail.

The project file can be thought of as a map of the project, containing references to all files and locations required for building your application or library. Like other source code files, this is both human-readable and machine-readable. The .pro file is the first place to look when you encounter "not found" or "undefined" messages during the build process (especially at link time). For further details we recommend that you read Qt's guide to qmake.

As you add more source-code, header, or library modules to your project, you must edit the .pro file and add the new items to the appropriate SOURCES, HEADERS, and LIBS lists. The same documentation standards that apply to C++ source code should be applied to project files (where comments begin with #).

In Example 1.6, you can see common qmake variable settings that can be helpful to add to all of your projects. By placing a copy of this file in a convenient location and adding the line

 include (/path/to/wherever you put/common.pri) 

to your project file, you can save yourself some typing.

Example 1.6. src/common.pri

# required if you want to see qDebug() messages
CONFIG += debug

# place auto-generated files in "invisible" subdirectories
win32 {
    MOC_DIR = _moc
    UI_DIR = _ui
    OBJECTS_DIR = _obj
} else {
    UI_DIR = .ui
    MOC_DIR = .moc
    OBJECTS_DIR = .obj
}

# rules below apply to TEMPLATE=app projects only:
app {
    # place executable in same folder:
    DESTDIR=$$OUT_PWD

    # don't place executables in an app bundle on mac os
    # this also permits console apps to work on the mac
    mac {
        CONFIG -= app_bundle
    }

    # Open a console for stdin, stdout, and stderr Windows:
    win32 {
        CONFIG += console
    }
}

[Note] CONFIG += console (for MSVC users)

The line, CONFIG += console, only necessary on Windows platforms, tells the compiler to build a "console" application, which can interact with the user via standard input/output streams. If you use MS Dev studio, this is equivalent to selecting Project Properties - Configuration Properties - Linker - System - Subsystem - Console Without this option, you will not see messages to qDebug(), stdout, or stderr.

[Note] CONFIG -= app_bundle (for Mac OSX)

For console apps on the Mac, you can prevent the creation of an "app bundle" that places your executable in a subdirectory by adding the line

CONFIG -= app_bundle

in your project file. That will also permit interactions with standard I/O. You can then use the command-line

qmake -spec macx-g++

to produce a Makefile. The command make> can then produce a single executable for your app.



[12] Depending on your development environment, you may find variants of make, such as mingw32-make, gmake, or cmake. In MS Dev Studio, you'll find nmake.

[13] To produce a Makefile in MacOSX, you need to add the line CONFIG -= app_bundle to the project file and you need to type qmake -spec macx-g++ instead of just qmake.