In Linux, a makefile is a text file that describes which files are required to build a particular program as well as how to compile and link the files to build the program. For a Linux program made up of several source and header files, the makefile specifies the following:

  • The items that make creates — usually the object files and the executable. Using the term target to refer to any item that make has to create is common.

  • The files or other actions required to create the target.

  • Which commands to execute to create each target.

Suppose that you have a C++ source file named form.C that contains the following preprocessor directive:

#include "form.h" // Include header file

The object file form.o clearly depends on the source file form.C and the header file form.h. In addition to these dependencies, you must specify how make converts the form.C file to the object file form.o. Suppose that you want make to invoke g++ (because the source file is in C++) with these options:

  • -c (compile only)

  • -g (generate debugging information)

  • -O2 (optimize some)

In the makefile, you can express these options with the following rule:

# This a comment in the makefile
# The following lines indicate how form.o depends
# on form.C and form.h and how to create form.o.
form.o: form.C form.h
g++ -c -g -O2 form.C

In this example, the first noncomment line shows form.o as the target and form.C and form.h as the dependent files.

The line following the dependency indicates how to build the target from its dependents. This line must start with a tab. Otherwise the make command exits with an error message, and you’re left scratching your head because when you look at the makefile in a text editor, you can’t tell the difference between a tab and a space.

Now that you know the secret, the fix is to replace the space at the beginning of the offending line with a single tab.

The benefit of using make is that it prevents unnecessary compilations. After all, you can run g++ (or gcc) from a shell script to compile and link all the files that make up your application, but the shell script compiles everything, even if the compilations are unnecessary.

GNU make, on the other hand, builds a target only if one or more of its dependents have changed since the last time the target was built. make verifies this change by examining the time of the last modification of the target and the dependents.

make treats the target as the name of a goal to be achieved; the target doesn’t have to be a file. You can have a rule such as this one:

rm -f *.o

This rule specifies an abstract target named clean that doesn’t depend on anything. This dependency statement says that to create the target clean, GNU make invokes the command rm -f *.o, which deletes all files that have the .o extension (namely, the object files). Thus, the effect of creating the target named clean is to delete the object files.