CIS 4307: Writing a Makefile

Makefile example

Makefiles are used to simplify the build process of a program. This is especially useful for programs that consist of many files and use many different libraries.

If used properly, a makefile will only recompile files that have changed. This is very useful in large systems that have a long build time. By recompiling only those files that have changed, the build time can be substantially reduced.

Basic Usage of Linux make

A make file usually has the filename 'makefile' or 'Makefile'. When running the 'make' tool, it searches for one of those two files and then performs the first target defined in that make file.

Parts of a Makefile


Targets are the main part of a make file. It specifies what should be created and how it is created. In general, the syntax of a target is as follows:

target: dependencies
(TAB)build instructions

Target specifies the file name of the target file that will be created.

Dependencies is a list of files that must exist before the target can be made. Dependencies are especially useful because make will only make the target if the dependencies are newer than the target. This basically means that if you haven't made any changes to the dependencies, the target won't be recompiled. This is a huge time saver in huge complex programs.

The first thing to note about build instructions is that they are preceded by a tab. After that, build instructions are simply commands. Any command you can use in the Linux shell, you can use as a build instruction. In addition to that, you are able to use builtin and custom-defined constants.

Custom-defined Constants

Usually, at the top of a make file you will find constant definitions. They are of the form:

name = value

To use a constant you type:


Everywhere in the make file where this is found will get replaced by the value associated with the name. This is useful if you have targets that share a common build option like the optimization option used by gcc.

Builtin Constants

Make files have special constants that you can use to specify dynamic values. The ones I find useful are $@ and $^. These constants are used within targets. $@ resolves to the target's name and $^ resolves to all the dependencies.