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 a universal makefile 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.

Project Name

The first rule is that the name of the project is either the name of the directory, unless the source is in a directory 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, the project name is "stlplus". This is because the directory containing the source 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 login to a different computer and compile again for the new platform. The makefile syatem keeps the working directories separate.

The directory name is in three parts. For example:

LINUX-alpha-debug

The first part of the name is the generic operating system name - in this case Linux. The operating system is put in uppercase to emphasise that the compiler macro LINUX will be defined as a compiler directive (in this case -DLINUX) and can be used in the source code in "#ifdef LINUX" 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)
make tidy
delete temporary working files such as object files
make clean
delete all generated files including executables

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.

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.

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 is smaller and more difficult to reverse engineer because it doesn't contain any debugger information.

Once a project is working and stable, you might want to run 'make tidy' to delete all intermediate files and leave just the make targets such as libraries or executables. If you want to remove all files generated by the make process, including the libraries and executables, use 'make clean' instead.

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 := ../stlplus
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 "make install" when you built wxWidgets so that the wx-config program is on the path.

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

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 project by building the two subprojects in order.

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.

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 up to two variables in your Makefile before the include to control what is to be built:

IMAGE := <path>

This specifies that you are making an executable image and the path gives both the directory and image name.

e.g. IMAGE := demo

Without this, the makefile makes a library only.

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 excluding "source" must be a directory name and must not be "..".

In addition to these key variables, there are other variables to control the build. These should be modified AFTER the include for this 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.

CFLAGS += <options>
CXXFLAGS += <options>

This sets extra options for the gcc compiler. The CFLAGS variable applies to C compiles (.c files) and CXXFLAGS applies to C++ compiles (.cpp files). These are typically extra include paths for libraries not managed by this make system and therefore not in the LIBRARIES list.

e.g. CXXFLAGS += -I../funnylib/include

LDFLAGS += <options>

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

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.

Examples

Here's a typical Makefile for building an 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: