Makefile Structure
It is just a plain text file named "Makefile" under a folder.
Bascially the structure is as follows:
target: dependencies
[tab] system command
For example, to compile all files,
all:
gcc -o main main.c add_int.c multi_int.c
Then you can type make in the terminal and main executable will be produced.
make
You can see calling make will invoke the target "all". make will run the first target as default.
Dependencies
By spliting the targets, modifying a single file will not recompile everything in the project. What you need to do is to specify the dependencies:
all: main
main: add_int.o multi_int.o main.o
gcc add_int.o multi_int.o main.o -o main
add_int.o: add_int.c
gcc -c add_int.c
multi_int.o: multi_int.c
gcc -c multi_int.c
main.o: main.c
gcc -c main.c
clean:
rm *.o main
Now you can see that the target all contains only a dependency. In order to fulfill the target all, it will go through the another dependency main and so on and finally all dependencies are fulfilled and compilation can take place.
The last target clean is a target to call rm to delete all compiled file.
PHONY Target
In the last example you can see a new target clean. Actually it is not a file target, instead it is a name that user wants to execute a particular command, like rm.
However, in rare case if the directory has a file called clean, make will be confused. As the file called clean exists, the target dependency is fulfilled and nothing will be run.
make: 'clean' is up to date.
In order to tell make clean is not a file target, you can explicitly declare the target to be "phony"
.PHONY: clean
all: main
main: add_int.o multi_int.o main.o
gcc add_int.o multi_int.o main.o -o main
add_int.o: add_int.c
gcc -c add_int.c
multi_int.o: multi_int.c
gcc -c multi_int.c
main.o: main.c
gcc -c main.c
clean:
rm *.o main
You can try, now make clean can be run successfully even if the file clean is here.
Variables
You can use variables in the Makefile to save time and get rid of repeated statements.
CC=gcc
CFLAGS=-Wall -c
all: main
main: add_int.o multi_int.o main.o
$(CC) add_int.o multi_int.o main.o -o main
add_int.o: add_int.c
$(CC) $(CFLAGS) add_int.c
multi_int.o: multi_int.c
$(CC) $(CFLAGS) multi_int.c
main.o: main.c
$(CC) $(CFLAGS) main.c
clean:
rm *.o main
In this case, we can specify the options for the compiler more easily.
Implicit Rule
In some cases, you can omit the command elements in the Makefile, and the make can automagically do the action for you.
CC=gcc
CFLAGS=-Wall
EXE=main
OBJ=add_int.o multi_int.o main.o
all: $(EXE)
main: $(OBJ)
clean:
rm -f $(OBJ) $(EXE)
In the target main, you can see only dependencies are declared. However, make can do all the compilation jobs. It is because there are implicit rules.
From the manual, it states:
n.o is made automatically from n.c with a recipe of the form ‘$(CC) $(CPPFLAGS) $(CFLAGS) -c’.
Therefore, add_int.o is made by using the above-mentioned rule automatically.
The brief introduction of Makefile stops here. Of course there are even more advanced features, please always refer to the Make documentation for more information.