Make a Makefile file to make make make files

The make utility is a general-purpose powerhouse whenever you need to, well, make a file or set of files based on some other files. It is of course not the only way to ‘make’ things in the English sense of the word; however, when you want to automate some process where one file is derived from others—building a program for example—you will benefit greatly from knowing a little bit about make. It can be taught to make almost anything, but it also comes standard knowing some very useful tricks, particularly when it comes to working with C programs.

A simple C program typically consists of one file named program.c, and you can run the C compiler to translate it into executable machine code in a file named program. The default rules for make include this pattern, so if you run make program and your working directory contains a program.c, the right thing will happen.

A C program of any significant complexity will usually be divided into multiple files. For example, you might break out some functions into their own C file, put your main function in its own C file, and have a header that is included in each of them. The default rules can handle what to do in this situation, but not how to guess which files should go together, so you will have to tell make a little about your project. When you run make, it will look in the working directory for a file named Makefile, and if that exists will read it to learn your custom rules.

You can make your Makefile with any text editor. The simplest sort of rule just lists which files can be made, and what other files they depend on. To build a C program with multiple source files, it is advantageous to compile each source file (with a .c extension) separately into an object file (with a .o extension), and then finally link all of the object files together into one program. It is implied that an object file depends on a source file with the same name, and that the program overall depends on the source file or object file with the same name, but any other dependencies (such as headers and other object files) should be listed.

program: functions.o
program.o: functions.h
functions.o: functions.h

Each line starts with a target file, a colon, and then a list of source files. Whenever you ask to build one of the target files, make will check when all the source files were last modified. (One of the nice things about make is that if the target was made more recently than any of the sources was modified, make will know that nothing needs to be done!) If the target does not exist yet or it needs to be remade, make will use its builtin recipe to create it fresh.

People who use make a lot for a lot of different projects usually add in some useful phony targets, so you can not only say make program to make program, but you can say make all to make everything in this project or make clean to delete extra files (like those intermediate .o object files). These are called ‘phony’ targets, because make all doesn’t actually make a file named all, it’s just a name for what you want to happen.

all: program
.PHONY: all

program: functions.o
program.o: functions.h
functions.o: functions.h

.PHONY: clean
clean:
    rm -f program program.o functions.o

The all target should always come first. Whatever rule comes first in the Makefile will be the default if you run just make without specifying a target, so putting all in that position makes sense. That way most of the time you only need to type four letters to make sure everything is up to date. The way you specify what ‘all’ means for this project is by listing the things you want to build after the colon, so all depends on them.

The clean target doesn’t depend on anything, but we do have to specify the recipe explicitly since there is no useful default. On the line after clean:, indent one tab (yes, it must be the tab character, not eight spaces or something else that looks similar) and then type shell commands. In our case, we want to remove (using rm) the built files, going back to the basic project with just its source files. The -f option to rm tells it not to complain if it is asked to delete something that already doesn’t exist.

There are other phony targets that people commonly provide, and in fact configuring your build system can become a whole rabbit hole, but this is enough for nearly all of our needs.

You have attempted of activities on this page