- 1. Basic Rule
- 2. Implicit rule & Pattern rule
- 3. Variables
- 4. Header file dependencies
1. Basic Rule
Suppose we have a project, structure is like this:
. ├── main.c ├── main.h ├── maze.c ├── maze.h ├── stach.h └── stack.c
stack is our self-implemented stack, and
maze is a maze game.
To compile them together, I can run
gcc main.c stack.c maze.c -o main.
If I modify
maze.c, then I need to re-compile all the files.
It's better to do it this way:
Then if I modified
maze.c, the only thing I need to do is:
To avoid typo, we can create a
The format of
makefile rule is:
target ... : prerequisites ... command1 command2 ...
The first rule in
makefile is the default rule.
For every command start with Tab in
make will create a Shell process to execute it.
make will only recompile the target that is modified, by checking the last modified time of source file
.h, and target file
To Summarize, target needs to be updated if one of the criteria is meet:
- target has not been run.
- one of the prerequisites needs to be updated
- some prerequisites's last modified time is later than current target
Normally Makefile will have a
So we can run
@ means does not display command itself, and only display the result. By default if one command fails, the target is terminated.
- means even if command fails, the target should continue.
There is a list of special built-in target, like
.PHONY. Check this link about GNU Special Targets
GNUMake also has some common target, that normally we follow the convention:
all: default target
clean: clean the generated binary files
The name of makefile is not required to be
Makefile, but it is recommended. The order of looking for makefile is
2. Implicit rule & Pattern rule
One rule can be written in multiple sub-rules, but only one rule should have the commands.
Example in first section can be written as:
There are some rules that can be omitted:
When we run
$ make cc -c -o main.o main.c cc -c -o stack.o stack.c cc -c -o maze.o maze.c gcc main.o stack.o maze.o -o main
If all the prerequisites of a target do not have command list, then
make will try built-in implicit rule. Use
make -p to list all implicit rules.
In the example, we have used:
CC is a makefile variable.
CC=cc is the declaration, and
$(CC) gets its value.
cc points to the C compiler. It could be
clang depends on your system.
TARGET_ARCH here are all empty.
COMPILE.c command becomes
$@ fetches the target in the rule, and
$< fetches the first prerequisites.
%.o: %.c is a pattern rule.
main.o matches this
%.o pattern, so
main. Thus it will run
cc -c -o main.o main.c.
maze.o also match
For multiple target rule:
target1 target2: prerequisite1 prerequisite2 command $< -o $@
target1: prerequisite1 prerequisite2 command prerequisite1 -o target1 target2: prerequisite1 prerequisite2 command prerequisite1 -o target2
In makefile, variable declaration can be put behind calling.
Expanded command is
gcc -O -g -Iinclude -c main.c.
CFLAGS: compilation options, like
CPPFLAGS: preprocessing options, like
When declaration is behind calling, it is easy to write calling cycle, like this:
A = $(B) B = $(A)
The value of
foo is determined in
all:, not at line 1.
If you want variable to be evaluated immediately, use
y := $(x) bar x := foo
y will be evaluated as
x is empty string at the time of evaluating.
?= means, only assign value if it has not been assigned. It is like
||= in Ruby.
+= is also allowed in makefile.
Other special variables:
$?: all the prerequisite that needs to be updated in a rule, as a list
$^: all the prerequisite, as a list
main: main.o stack.o maze.o gcc main.o stack.o maze.o -o main
can be writter as:
This is for build library file.
ar is a archive tool.
Some frequently used makefile variables:
|Variable Name||Default value||Description|
||C++ compiler name|
||C preprocessor name|
||target platform options|
|$(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)`||link
||compile c files|
||compile cc files|
4. Header file dependencies
Our makefile looks like this now:
all: main main: main.o stack.o maze.o gcc $^ -o $@ main.o: main.h stack.h maze.h stack.o: stack.h main.h maze.o: maze.h main.h clean: -rm main *.o .PHONY: clean
One problem is that, we need to check the source code to determine header files dependencies. If we update source file, we may forget to update makefile.
gcc -M auto generate target file and source file dependencies:
$ gcc -M main.c main.o: main.c /usr/include/stdio.h /usr/include/features.h \ /usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs.h /usr/include/gnu/stubs-32.h \ /usr/lib/gcc/i486-linux-gnu/4.3.2/include/stddef.h \ /usr/include/bits/types.h /usr/include/bits/typesizes.h \ /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \ /usr/lib/gcc/i486-linux-gnu/4.3.2/include/stdarg.h \ /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h main.h \ stack.h maze.h
If we don't want system header file dependencies, use
$ gcc -MM *.c main.o: main.c main.h stack.h maze.h maze.o: maze.c maze.h main.h stack.o: stack.c stack.h main.h
Next problem is, how to put the dependencies into makefile:
$(sources:.c=.d) is a substitution syntax, which means substitute all the
include $(sources:.c=.d) becomes:
include main.d stack.d maze.d
include means read other makefiles.
You don't have
.d files for now, so
make tries the pattern rule
%.d: %.c. The four lines below, uses only one make process to run it.
set -e means, in current process, if any of the command returns non-zero, the process terminates.
rm -f $@ removes previous generated
$$ is the process id. We use
sed tool to generate
make command options
-nprint the compiled command, but not execute it.
-Cis used change directory and run a makefile there.