Makefile, qmake, and Project Files

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. During the testing/development phase, the project is recompiled and re-linked many times. To produce an executable, all changed source files must be recompiled, and the object files must all be re-linked.

Keeping track of all of 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. It 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):

  • Rules for building certain kinds of files (e.g., to get a .o file from a .cpp file, we must run gcc -c on the .cpp file)
  • Targets that specify which executables (or libraries) must be built
  • Dependencies that list which targets need to be rebuilt when certain files get changed

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 need to be rebuilt, rather than blindly recompiling every source file every time. Figure 3.1 is a diagram that attempts to show the steps involved in building a Qt application. To learn more about make, we recommend the book The Linux Development Platform by Rafeeq Ur Rehman and Christopher Paul (Prentice Hall).

Figure 3.1. (q)make build steps

With Qt, it is not necessary 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) under the covers and either display or filter its output.

The following transcript shows which files get created at each step of the build process for Example 3.1.

src/qapp> ls -sF
total 296
 4 main.cpp
src/qapp> qmake -project
src/qapp> ls -sF
total 296
 4 main.cpp 4
src/qapp> qmake
src/qapp> ls -sF
total 296
 4 main.cpp 4 4 Makefile
src/qapp> make
g++ -c -pipe -g -Wall -W # compile step
 -I/usr/local/qt/mkspecs/linux-g++ -I.
 -I/usr/local/qt/include/QtGui -I/usr/local/qt/include/QtCore
 -I/usr/local/qt/include -I. -I. -I.
 -o main.o main.cpp
g++ -Wl,-rpath,/usr/local/qt/lib # link step
 -L/usr/local/qt/lib -L/usr/local/qt/lib -lQtGui_debug
 -L/usr/X11R6/lib -lpng -lXi -lXrender -lXinerama -lfreetype
 -lfontconfig -lXext -lX11 -lm -lQtCore_debug -lz -ldl -lpthread
 -o qapp main.o
src/qapp> ls -sF
total 420
 4 main.cpp 132 main.o 124 qapp*
 8 Makefile 4

Notice that we can see the arguments passed to the compiler when we run make. If any errors are encountered, we will see them too.

3.2.1. #include: Finding Header Files

There are three commonly used ways to #include a library header file:

#include "headerFile"
#include "path/to/headerFile"

The angle brackets (< >) indicate that the preprocessor must look (sequentially) in the directories listed in the include path for the file.

A quoted filename indicates that the preprocessor should look for headerfile in the including file's directory first. A quoted path indicates that the preprocessor should check the path directory first. The path information can be absolute or relative (to the including file's directory). If no file is found at the specified location, the directories listed in the include path are searched for headerfile.

If versions of the file exist in more than one directory in the include path, the search will stop as soon as the first occurrence of the file has been found. If the file is not found in any of the directories of the search path, then the compiler will report an error.

For items in the C++ Standard Library, the compiler generally already knows where to find the header files. For other libraries, we can expand the search path by adding a -I/path/to/headerfile switch to the compiler.

If you use an IDE, there will be a Project->Settings->Preprocessor, or Project->Options->Libraries configuration menu that lets you specify additional include directories, which end up getting passed as -I switches to the compiler.

With qmake, as we will soon see, you can add INCLUDEPATH += dirname lines to the project file. These directories end up in the generated Makefile as INCPATH macros, which then get passed on to the compiler/preprocessor at build time.

In general, it is a good idea to #include non-Qt header files after Qt header files. Since Qt does define many symbols, in the compiler as well as the preprocessor, this helps you avoid/find name clashes more easily.


3.2.2. The make Command

Instead of running the command line compiler directly, we will start using make,[2] which greatly simplifies the build process when a project involves multiple source files and libraries.

[2] Depending on your development environment, this program goes under many other names, such as mingw32-make, nmake, gmake, or unsermake.

We have seen before that qmake (without arguments) reads a project file and builds a Makefile. Example 3.3 is a slightly abbreviated look at the Makefile for the previous qapp project.

Example 3.3. src/qapp/Makefile-abbreviated

# Excerpts from a makefile

####### Compiler, tools and options

CC = gcc # executable for C compiler
CXX = g++ # executable for c++ compiler
LINK = g++ # executable for linker

# flags that get passed to the compiler
CFLAGS = -pipe -g -Wall -W -D_REENTRANT $(DEFINES)
INCPATH = -I/usr/local/qt/mkspecs/default -I. 
 -I$(QTDIR)/include/QtGui -I$(QTDIR)/include/QtCore 

# Linker flags
LIBS = $(SUBLIBS) -L$(QTDIR)/lib -lQtCore_debug
 	 -lQtGui_debug -lpthread
LFLAGS = -Wl,-rpath,$(QTDIR)/lib

# macros for performing other operations as part of build steps:
QMAKE = /usr/local/qt/bin/qmake

####### Files

HEADERS = # If we had some, they'd be here.
SOURCES = main.cpp
OBJECTS = main.o
TARGET = qapp # default target to build

first: all # to build "first" we must build "all"

####### Implicit rules

.SUFFIXES: .c .o .cpp .cc .cxx .C

$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<

## Possible targets to build

all: Makefile $(TARGET) # this is how to build "all"

$(TARGET): $(OBJECTS) $(OBJMOC) # this is how to build qapp
 qmake: FORCE # "qmake" is a target too!

 @$(QMAKE) -o Makefile # what does it do?

dist: # Another target
 @mkdir -p .tmp/qapp 
 && $(COPY_FILE) --parents $(SOURCES) $(HEADERS) 
 $(FORMS) $(DIST) .tmp/qapp/ 
 && (cd 'dirname .tmp/qapp'  && $(TAR) qapp.tar qapp 
 && $(COMPRESS) qapp.tar) 
 && $(MOVE) 'dirname .tmp/qapp'/qapp.tar.gz . 
 && $(DEL_FILE) -r .tmp/qapp

clean:compiler_clean # yet another target
 -$(DEL_FILE) *~ core *.core

####### Dependencies for implicit rules

main.o: main.cpp

The command make checks the dependencies and performs each build step specified in the Makefile. The name and location of the final result can be set with the project variables, TARGET and target.path. If TARGET is not specified, the name defaults to the name of the directory in which the project file is located. If target.path is not specified, the location defaults to the directory in which the project file is located.

3.2.3. Cleaning Up Files

make can clean up the generated files for you with the two targets clean and distclean. Observe how they are different from the following code:

src/qapp> make clean
rm -f main.o
rm -f *~ core *.core
src/qapp> ls
 Makefile main.cpp qapp

src/qapp> make distclean
rm -f qmake_image_collection.cpp
rm -f main.o
rm -f *~ core *.core
rm -f qapp
rm -f Makefile
src/qapp> ls

After a make distclean, the only files that remain are the source files that can go into a tarball for distribution.

If you modify a project file since the last execution of make, the next invocation of make should rebuild the Makefile itself (via qmake) before re-running make on the newly generated Makefile.

In other words, the Makefile is qmake-aware and can re-qmake itself.

The command make dist will create a tarball (dirname.tar.gz) that contains all the source files that the project file knows about.

As we add more source-code, header, or library modules for our project, we edit the .pro file and add the new items to the 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 #).

We think of the project file as a map of our project, containing references to all files and locations required for building our 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 we encounter "not found" or "undefined" messages during the build process (especially at link time). For further details we recommend that you read Trolltech's guide to qmake.[3]


Part I: Introduction to C++ and Qt 4

C++ Introduction


Introduction to Qt



Inheritance and Polymorphism

Part II: Higher-Level Programming


Introduction to Design Patterns


Generics and Containers

Qt GUI Widgets


Validation and Regular Expressions

Parsing XML

Meta Objects, Properties, and Reflective Programming

More Design Patterns

Models and Views

Qt SQL Classes

Part III: C++ Language Reference

Types and Expressions

Scope and Storage Class

Statements and Control Structures

Memory Access

Chapter Summary

Inheritance in Detail

Miscellaneous Topics

Part IV: Programming Assignments

MP3 Jukebox Assignments

Part V: Appendices

MP3 Jukebox Assignments


MP3 Jukebox Assignments

An Introduction to Design Patterns in C++ with Qt 4
An Introduction to Design Patterns in C++ with Qt 4
ISBN: 0131879057
EAN: 2147483647
Year: 2004
Pages: 268

Similar book on Amazon © 2008-2020.
If you may any questions please contact us: