Manual

Introduction

The makefiles project is a set of universal makefiles for building any project with Gnu tools (gcc, Gnu make etc.). The idea is that you write a trivial Makefile which simply includes these universal makefiles and it does the rest for you. This makes it very easy to set up new projects and to ensure consistency of builds in a multi-library project.

These rules do the following tasks:

Note: These files are designed for ***Gnu make ONLY***.

A make file contains a set of rules. The scope for writing rules is limited by the makefile syntax. Therefore it has been necessary to restrict the flexibility of the universal makefile in order for it to be possible to write it at all.

Writing a Makefile

The gcc.mak file should be included in your Makefile to set up the default make rules for gcc.

include ../makefiles/gcc.mak

Or whatever the path to the gcc.mak file is. A relative path rather than an absolute path is recommended.

You need to define variables in your Makefile before the include to control what is to be built:

IMAGE := <path>

This specifies that you are making a program (executable image) and the path gives both the directory and image name.

e.g. IMAGE := demo

SHARED := <name>

This specifies that you are making a shared library and the name is the raw name of the library i.e. without the lib prefix or .so suffix.

e.g. SHARED := demo

This will create a shared library called libdemo.so.

neither IMAGE nor SHARED

Without IMAGE or SHARED, the makefile makes a static library containing all the compiled object files.

LIBRARIES := <path> ...

A space-separated list of library directories to include/link into the build. Only libraries managed by this make system and set of rules should be in the list. See later for how to manage third-party libraries.

e.g. LIBRARIES := ../stlplus3/containers

Each path should point to the source directory of a project which is also managed by this makefile system.

Note that the part of the path used to create the project name - i.e. the last part must be a directory name and must not be "..". In this case the project name is "containers".

e.g. LIBRARIES := ../stlplus3/source

In this case the "/source" suffix is ignored and the project name is "stlplus3". This project name must be a directory name and must not be "..".

In addition to these key variables which control what is to be built, there are other variables to control the details of the build. These modifier variables should be added AFTER the include for the gcc.mak file. The most useful are those that allow other libraries to be incorporated into the build that are not managed by this make system.

The variables that can be modified are:

CPPFLAGS += <options>

Preprocessor flags common to both C and C++ compiles. The most common and useful of these are extra include paths for libraries not managed by this make system and therefore not in the LIBRARIES list.

CFLAGS += <options>

This sets extra options for the gcc compiler when compiling C code (.c files) only. These are typically C language specific options.

e.g. CFLAGS += -O3

CXXFLAGS += <options>

This sets extra options for the gcc compiler when compiling C++ code (.cpp files) only. These are typically C++ language specific options.

e.g. CXXFLAGS += -ftemplate-depth-50

LDFLAGS += <options>

Sets extra flags for the linker. These are placed before the object files in the link command

e.g. LDFLAGS += -s

LOADLIBES += <objects>

Extra object files or libraries to be incorporated into the link which are not managed by this make system. These are placed after the object files in the link command.

The LOADLIBES variable can contain names of object files or archive libraries:

e.g. LOADLIBES += -lxyz

So in the above case the library being linked against is libxyz.a.

This options is also used to create a dependency on shared libraries (.dll on Windows or .so on Unixes):

e.g. LOADLIBES += -lxyz

So in the above case the library being linked against is either xyz.dll on Windows or libxyz.so on Unix.

You may need to use the uppercase -L to specify a search path for these libraries:

e.g. LOADLIBES += -L../xyz -lxyz

Note the use of '+=' not ':='. You are adding extra options to the existing options created by the make system.

There are also some variables that can be used to switch on or off certain options. These take the values "on" or "off":

RELEASE=on | RELEASE=off (default)

When switched on, builds a release version of the application. This has no debugging information and compiler optimisation is turned up to generate a small, fast program. When switched off, this builds a debug version, with full debugger information for use with gdb, and with compiler optimisations disabled. Note: this is usually swiched on from the command line, rather than from the Makefile: see Building Variants.

GPROF=on | GPROF=off (default)

When switched on, builds a profiling version of the application for use with the gprof profiler. When switched off, this builds a normal version. Note: this is usually swiched on from the command line, rather than from the Makefile: see Building Variants.

UNICODE=on | UNICODE=off (default)

When switched on, enables Unicode character support. In fact, this just enables compiler directives -DUNICODE and -D_UNICODE, so it is up to the programmer to ensure that these are tied to Unicode support. Most libraries follow this convention.

STATIC=on | STATIC=off (default)

When switched on, causes the compiler to link, where possible, with static C and C++ libraries to minimise dependencies on shared libraries. This removes a dependency on the C++ STL shared library and may remove other dependencies too, depending on the application. You still need to check for any remaining unresolvable shared library dependencies. If switched off, the build will use shared libraries where possible to minimise that application size.

VERBOSE=on | VERBOSE=off (default)

When switched off, gives a brief one-line summary of each build command, unless there are errors in which case a standard error report is given. When switched on, the build commands are switched into verbose mode to give more diagnostics.

Make Targets

There are several targets that can be used with make. The idea is that you run make from the command line with a target like this:

$ make <target>

The target is optional; if missing then the default target is used.

The targets currently supported by the Makefiles project are:

all (default)

Equivalent to target 'build', but there are circumstances where you can change the set of targets mapped onto the 'all' target.

build

Compile all source files and link the image if there is one (i.e. if the IMAGE variable is set) or the shared library if there is one (i.e. SHARED is set).

run

Run the program if there is one (i.e. if the IMAGE variable is set). Will run the build target first.

tidy

Delete intermediate files created during the compile.

clean

Delete all files created during the compile, including temporary files, libraries and the program if there is one.

This is useful if you have made changes to the compile options and need to start from clean.

Targets can be combined on the command line. For example, to build and run a project:

$ make build run

Or even, to delete all generated files, build from stratch and then run:

$ make clean build run

Project Name

The name of the project is either the name of the directory, unless the directory is called "source" in which case the directory above that is used. The makefile uses the name of project to generate the library name.

For example, here is the directory structure of the STLplus project:

In the example above, for the Makefile shown in bold, the project name is "stlplus". This is because the directory containing the Makefile is called "source" and so the directory above that is used. This directory is called "stlplus".

A simpler directory structure could have been used:

In this case, the project name is still "stlplus". This is because the directory containing the source is called "stlplus" and so this is used as the project name.

Project names must be unique within a particular application's development. For example, you might be working on an application which is split into several projects for coding purposes. Each project that makes up the application must have a different project name.

Within a project, all the source code - that is, all headers (.h or .hpp) and implementation files (.c or .cpp) - must be in one directory. The Makefile should be in this same directory. A multiple-directory structure should be built as multiple projects, one per directory.

Source Files

The universal makefile uses file extensions to determine the type of compilation to use:

.c
C source
.cpp
C++ source
.rc
resource definitions (Windows only)

Any file with these extensions in the project directory will be compiled using the appropriate compiler for its type.

So, if you use .c extensions for C++ files you will have problems. I believe it is good practice to be clear whether a file is C or C++ and so this is a feature, not a bug.

I also recommend:

.h
a C header
.hpp
a C++ header
.tpp
a C++ template implementation

Working Directory

When you compile files with the universal makefile, it stores all compiled object code in a subdirectory of the source directory. You can clean up by simply deleting this working directory.

The name of the working directory is derived from the operating system and CPU type of the computer you are working on. The universal makefile contains a set of rules for working out this name.

The reason for doing this is that some people work on the same source code with different compilers and possibly even with disks that are accessed over a network from one of a number of different computers. The makefile system keeps the compilations for different platforms separate. This means that you can, for example, compile your code on one platform, then compile again for another platform. The makefile syatem keeps the working directories separate.

The directory name is in three parts. For example:

GNULINUX-alpha-debug

The first part of the name is the generic operating system name - in this case Gnu/Linux. The operating system is put in uppercase to emphasise that the compiler macro GNULINUX will be defined as a compiler directive (in this case -DGNULINUX) and can be used in the source code in "#ifdef GNULINUX" preprocessor directives.

The second part of the name is the CPU type - in this case "alpha" which is the DEC Alpha 64-bit CPU.

The third part of the name is the build variant - in this case "debug". This means that this is a debug build of the software - see the next section for more details of build variants and how to create them.

Building Variants

The make system can build different variants of a project depending on the argument following make.

make
build a debug version (debug code, no optimisations)
make RELEASE=on
build a release version (no debug code, highly optimised)
make GPROF=on
build a profiling version (same parameters as the release version)

Note that these variants are created by specifying values for variables used to control the build type. See the definitions above for the variables RELEASE, GPROF, UNICODE, STATIC and VERBOSE. In fact any variable controlling a build can be set either in a Makefile, meaning always use the same value, or from the command line, in which case it is a one-off build with that option.

The idea is that you develop using the debug version. This has all symbolic information required to run a debugger such as Gnu's "gdb" as well as all macros controlled by the NDEBUG preprocessor directive. The NDEBUG directive is the ANSI C standard method for including/excluding debugging code in your source - it is used by the C macro assert(expr) and also the extended debugging macros provided by the STLplus library header debug.hpp.

You can at any time build a profiling variant for use with Gnu's "gprof". This allows you to identify any performance problems and optimise code selectively based on performance profiles. Typically 95% or more of a program is NOT performance critical, so optimising everything is a huge waste of effort. Profiling allows you to focus on the 5% of the program that can actually benefit from optimisation.

Note that the profiling version is built with the same optimisation level as the release version to give an accurate feel for how the release version will perform.

Once you are satisfied that your program is entirely bug-free (okay, only joking) and blindingly fast (yeah, okay, joking again) you can then build a release variant for shipping to the customer. This will not only run about four times faster than the debug variant but will be much smaller too. It is also more difficult to reverse engineer because it doesn't contain any debugging information.

Subdirectories

There is a separate set of make rules for handling subdirectory structures. For example, to make a project with two subprojects, you might have the following structure:

So, I want to write the Makefile (highlighted in bold above) for the top-level "project" directory that builds the main project by building the two subprojects.

Create a project/Makefile containing one line:

include ../makefiles/subdirectories.mak

This rule searches for the set of subdirectories that themselves contain a Makefile and makes them.

Using wxWidgets

There is an additional universal makefile for use when building wxWidgets programs. This makefile should be included after the gcc.mak file.

For example:

IMAGE     := demo
LIBRARIES := ../stlplus3
include ../makefiles/gcc.mak
include ../makefiles/wx.mak

The wx.mak file adds extra compiler and linker options for wxWidgets. It uses the wx-config script provided with wxWidgets to adapt to your installation - in other words, you need to have run "configure", "make" and "make install" when you built wxWidgets so that the wx-config program is on the path.

Debug and Release builds

When building a debug version of the application (make), the makefile uses the debug configuration of wxWidgets (wx-config --debug=yes) and likewise a release version (make RELEASE=on) gets the release configuration of wxWidgets (wx-config --debug=no).

Shared Library dependencies

The default is to build a wxWidgets application using shared libraries for the wxWidgets libraries. This is consistent with the default configuration of wxWidgets. However, you can build the application without any wxWidgets shared library dependencies - as a static build - by specifying the WXSTATIC option in the Makefile:

IMAGE     := demo
LIBRARIES := ../stlplus
WXSTATIC  := on
include ../makefiles/gcc.mak
include ../makefiles/wx.mak

For this to build correctly, you must have a non-shared version of wxWidgets installed, which is done by building wxWidgets with the "--disable-shared" option for the configure script.

Note: this is different from the STATIC option - which controls whether the application depends on the C++ shared libraries. The WXSTATIC option controls whether the application depends on the wxWidgets shared libraries.

Selecting a wxWidgets version

It is possible to have several different versions of wxWidgets installed at the same time and to select which one to use at build time. This selection is supported via the WXVERSION option in the Makefile:

IMAGE     := demo
LIBRARIES := ../stlplus
WXSTATIC  := on
WXVERSION := 3.0
include ../makefiles/gcc.mak
include ../makefiles/wx.mak

This will select version 3.0 of wxWidgets and build the application with it.

Examples

Here's a typical Makefile for building a static object library:

LIBRARIES := ../stlplus3/portability
include ../makefiles/gcc.mak

Here's a typical Makefile for building an executable image:

IMAGE     := ../bin/ccolour
LIBRARIES := ../stlplus3/portability
include ../makefiles/gcc.mak

To show the whole picture, here's a multi-library project for the "ccolour" program with the directory structure and makefiles used to build it. The ccolour project uses the stlplus3 project. To build the whole project, all you have to do is to go into the source directory of the ccolour directory and run make: