Skip to content

Latest commit

 

History

History
 
 

Makefiles

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 

#GNU Make

The make tool automates the process of building a project from its source code. For make to work, your need to add a file named Makefile into your project's root folder. In this tutorial, we provide five example Makefiles.

Example 1: Hello World

Here are the contents of our first Makefile:

hello:
    cc -o hello helloworld.c

evil:
    rm -rf $HOME

A line that ends with a colon (:) creates a target. The indented lines following a target are called a "recipe." In this case, our first target is called hello, and its recipe is cc -o hello helloworld.c. When you type the command

$ make hello

in the terminal, make opens the Makefile, finds the target hello, and runs the corresponding recipe. In this case, we run the command cc to compile a program. cc stands for "C Compiler" and is the default system compiler for C source code. If you're compiling C++ code, then you would use g++ instead.

You should never trust Makefiles you download from the internet. Makefiles can execute arbitrary commands. If you run

$ make evil

you will delete your home directory!

When you run:

$ make

without specifying a target, the first target gets executed. Therefore, when you're creating your Makefiles, the first target should be the most important.

Example 2: Prerequisites

The full syntax for rules includes something called "prerequisites":

[targets]: [prerequisites]
    [recipe]

Here is an example Makefile that uses prerequisites:

# first rule
program: main.o file.o
    cc -o program main.o file.o

# second rule
main.o: main.c file.h
    cc -c main.c

# third rule
file.o: file.c
    cc -c file.c

When make or make program is run, the first rule is processed. Since its prerequisites are targets, the rules for main.o and file.o are executed before the program recipe.

If main.o doesn't exist, make executes the recipe for the second rule. But if main.o already exists, make executes the recipe only if main.c or file.h is more recent than main.o. This prevents us from wasting time recompiling files that haven't changed. On a large project, this can save hours of waiting.

After the prerequisites main.o and file.o are built, if program does not exist, the recipe for the first rule is executed and the object files are linked. Otherwise, make executes the recipe only if main.o or file.o is more recent than program.

If the target from make [target] already exists and none of its prerequisites are more recent than it, make will report that the target is already up to date and do nothing.

For the first example, no prerequisites were used. If a file named hello already exists, then even after modifying the hello.c file, make will believe that the target is up to date and won't execute the recipe.

The # symbol is used for comments. Everything after # will be ignored.

Example 3: Variables and Implicit Rules

Variables are used for readability and to avoid repetition. The syntax for setting a variable is:

name = expression

and we use a variable by surrounding it with $( and ).

In our third Makefile, we create variables objects and CFLAGS:

objects = main.o file.o

CFLAGS = -ansi -pedantic -Wall -Werror

program: $(objects)
	cc -o program $(objects)

main.o: file.h
file.o:

clean:
	rm -f program $(objects)

In this example, the .c file prerequisites and recipe lines for the object files have been omitted. make has implicit rules for generating .o files, so it is unnecessary to spell it out. CC and CFLAGS are built-in variables that are automatically used when implicitly compiling C code. CXXFLAGS is used for C++ and CPPFLAGS is used for both C and C++.

In this example, even though there is no explicit recipe for generating file.o, make will use implicit rules to execute $(CC) $(CPPFLAGS) $(CFLAGS) -c -o file.o file.c.

There are a few other new things to note in this Makefile.

The recipe for clean breaks the convention that a rule builds its target. Instead, it removes all the files generated by make. In this way, the relevant directories become "clean."

rm -f ignores nonexistent files instead of printing an error and never prompts for confirmation before removing a file.

Example 4: Functions and Automatic Variables

For greater generalization and automation, functions can be used.

The wildcard function uses the syntax $(wildcard [pattern]). It returns a list of space-separated files that match the pattern. The * character matches any string. In this example, $(wildcard *.c) is replaced by all .c files: file.c main.c.

The patsubst function uses the syntax $(patsubst [pattern], [replacement], [text]). It replaces all whitespace-separated words in [text] that match [pattern] with [replacement]. The % character matches any string for all instances of % in an expression. In this example, the pathsubst function is replaced by file.o main.o.

$@ is the target of the rule. In this example, cc -o $@ $(objects) is equivalent to cc -o program $(objects).

%.o: matches all object files. Because main.o requires file.h in addition to main.c, that prerequisite is indicated in the next line. The implicit rules handle the rest.

The rule for clean has been modified to use the wildcard. It is subtly different from the previous version, which removed all files specified by objects -- this version removes all files ending in .o.

Makefile 4:

CFLAGS = -ansi -pedantic -Wall -Werror
objects = $(patsubst %.c, %.o, $(wildcard *.c))

program: $(objects)
	cc -o $@ $(objects)

%.o:
main.o: file.h

clean:
	rm -f program *.o

Example 5: Directories

Files are sometimes organized by their type. One setup is to put all the source code files in a directory called src, object files in a directory called obj, and binary executable in a directory called bin.

VPATH is a variable that tells make which directories to search for target and prerequisite files in addition to the working directory. In this example, make will search src/ in addition to the working directory.

Multiple directories are delimited by :. For example, VPATH = src1:src2:../src3 tells make to search src1/, src2/, and ../src3 in addition to the working directory.

The addprefix function uses the syntax (addprefix [prefix], [names...]). It prepends [prefix] to each name in the whitespace-separated [names...] and returns the result. In this example, the addprefix function is replaced by obj/main.o obj/file.o.

Order-only prerequisites are listed after |. Unlike normal prerequisites, make does not check whether it is more recent than the target. The syntax for rules is updated to:

[targets]: [normal prerequisites] | [order-only prerequisites]
    [recipe]

Whenever a file is added to, removed from, or renamed in a directory, the timestamp of the directory is updated. It is inefficient to rebuild a target just because the timestamp of its prerequisite directory was updated. That is why it is an order-only prerequisite.

$< is the first prerequisite of the rule. In this example, it would be the C source file corresponding to the target object file.

Makefile 5:

VPATH = src
CFLAGS = -ansi -pedantic -Wall -Werror
objects = $(addprefix obj/, main.o file.o)

bin/program: $(objects) | bin
    cc -o $@ $(objects)

obj/%.o: %.c
    cc $(CFLAGS) -c -o $@ $<
obj/main.o: file.h

$(objects): | obj

bin:
    mkdir bin

obj:
    mkdir obj

clean:
    rm -rf obj bin