1. The Three Essential Elements of Makefile
2. Working Principle
3. Start Writing
First, let’s write our program, taking C language as an example.
1) func.h
Define two functions: addition and subtraction:
2) Implementation of the Addition Function
3) Implementation of the Subtraction Function
4) main function
3.1 Version 1
The most basic version: just write it directly, a simple explanation: when we compile the above files, we use gcc -o main main.c add.c sub.c.
Then we convert it into a Makefile, following the above working principles and rules.
For example, sub.o depends on sub.c generated by the gcc -c command, and this dependency can be achieved by:
sub.o: sub.c
Then write the implementation command on the second line, noting that the second line must strictly follow the tab control similar to Python syntax. The rest are similar operations.
3.2 Version 2
We find that Version 1 is too long; how can we optimize it? We can use the characteristics of the language, and Makefile has variables for assignment. Assign all .o files to the obj variable and main to the target variable.
Referencing these variables is done in the form of: $(target). $(variable).
The last two lines explain the automatic variables mentioned earlier:
$<: The first dependency in the rule
$@: The target in the rule
$^: All dependencies in the rule
So the last line becomes as shown in the figure, where %.o and %.c match each .o and .c file. Modifying Version 1 becomes as shown in the figure:
3.3 Version 3
Introduce the CC variable. In Makefile, there are some built-in variables, such as CC, which we can assign and modify, or use directly. Further modify Version 2 to become Version 3.
3.4 Version 4
Here we introduce two commonly used functions in Makefile: wildcard and patsubst, which are used for finding local files and pattern matching, respectively.
src= Find all .c files locally, obj= Replace all .c files locally with all .o files.
3.5 Version 5
When using Makefile, we need to clean all .o and intermediate files; how can we implement this in Makefile?
By using clean, and the normal clean syntax is:
clean:
rm *.o main
If we run clean twice in a row, we will encounter problems, as shown in the figure:
This issue arises because you deleted once, and the local disk does not have the corresponding file, which leads to an error. The solution to this problem is to add the -f parameter to force it.
clean:
rm *.o main -f
When we create a clean file locally and then run make clean, it always shows the latest, as shown in the figure:
Here we introduce a concept: phony targets. Because make clean will compare the local disk with the files after make clean, and make clean does not generate corresponding files, according to the characteristics of Makefile, every time after an update, it must be new, while the local disk is existing, so there will definitely be a mismatch, resulting in the above result.
Targets like make can generate files, while make clean does not generate files, are called phony targets, and at this time, we need to declare this in Makefile:
.PHONY: clean
clean:
rm *.o main
Finally, when we want to do other tasks under clean, such as creating files, if there are no permissions or other issues, the following commands will not be executed.
For example, creating a file under /usr/local where ordinary users do not have permissions will inevitably result in an error:
.PHONY: clean
clean:
mkdir /usr/local/a
rm *.o main
It will directly report an error. The solution to this problem is simple: add a hyphen (-) before the command line.
.PHONY: clean
clean:
-mkdir /usr/local/a
rm *.o main
Finally, the complete Makefile is as follows: