Click the above “Baijun Technology“, select “Top Public Account”
Embedded essentials, delivered promptly
ARM bare-metal phase 1 enhanced video course accompanying WiKi lesson 9, section 5: Introduction and Rules of Makefile.
Text cannot completely replace video, so if you find these articles hard to understand, it is recommended to purchase the video for further learning.
Video purchase link: 100ask.taobao.com.
When using tools like keil, mdk, avr to compile programs, you can just click the mouse; what is the internal mechanism? How does it organize and manage programs? How does it decide which file to compile?
Answer: In fact, IDE development tools like keil manage programs using Makefile. When developing bare-metal programs under linux, Makefile is used to organize and manage these programs. This section explains the most basic rules of Makefile.
What does Makefile do? It organizes and manages programs and files, so let’s write a small program to experiment:
File a.c
#include <stdio.h>
int main()
{
func_b();
return 0;
}
File b.c
#include <stdio.h>
void func_b()
{
printf(“This is B\n”);
}
Compile:
gcc -o test a.c b.c
Run:
./test
Result:
This is B
The command gcc -o test a.c b.c is simple, but the functionality it accomplishes is not simple.
Let’s see what it does.
We know that a .c program needs to go through four steps to obtain an executable:
1. Preprocessing
2. Compilation
3. Assembly
4. Linking
We often refer to the first three steps collectively as compilation. Let’s analyze this command: gcc -o test a.c b.c
It goes through the following steps:
1) For a.c: execute the preprocessing, compilation, and assembly process, resulting in a.c > xxx.s > xxx.o.
2) For b.c: execute the preprocessing, compilation, and assembly process, resulting in b.c > yyy.s > yyy.o.
3) Finally: xxx.o and yyy.o are linked together to produce an application test.
Tip: gcc -o test a.c b.c -v: adding a ‘-v’ option allows you to see the processing steps.
On the first compilation of a.c, the xxx.o file is generated, which is reasonable. After executing the first time, if a.c is modified and gcc -o test a.c b.c is executed again, xxx.o should be regenerated for a.c, but b.c will be recompiled again, which is completely unnecessary: b.c has not been modified, and the previously generated yyy.o file can be used directly.
One disadvantage of not using Makefile: All files will be processed again, even if b.c has not been modified, b.c will be recompiled again. When there are few files, this is not a problem, but when there are many files, the compilation efficiency will be very low.
If there are many files and only one file is modified, all files will be recompiled, leading to a long compilation time.
For these source files, we should handle them separately, executing preprocessing, compilation, and assembly first, then linking them together, for example:
Compile:
gcc -o a.o a.c
gcc -o b.o b.c
Link:
gcc -o test a.o b.o
For the above example, when we modify a.c, a.c will be recompiled, and then they can be linked together; b.c does not need to be recompiled.
Now the question arises, how to know which files have been updated/modified?
Compare time: compare a.o and a.c timestamps; if a.c is newer than a.o, it indicates that a.c has been modified.
Similarly, b.o and b.c will also be compared. Compare the timestamps of test with a.o and b.o; if a.o or b.o is newer than test, it indicates that test should be regenerated.
Makefile does exactly that.
Now let’s write a simple Makefile:
The most basic syntax of Makefile is the rule, where the rule is:
Target : Dependency 1 Dependency 2 …
[TAB] Command
When the “dependencies” are newer than the “target”, execute the commands below them. We write the three commands above as Makefile rules as follows:
test: a.o b.o // test is the target, it depends on a.o b.o files. If a.o or b.o is newer than test, the commands below need to be executed to regenerate the executable test.
gcc -o test a.o b.o
a.o: a.c // a.o depends on a.c; when a.c is newer, execute the command below to generate a.o
gcc -c -o a.o a.c
b.o: b.c // b.o depends on b.c; when b.c is newer, execute the command below to generate b.o
gcc -c -o b.o b.c
Now let’s do an experiment:
Write a Makefile file in this directory:
File: Makefile
test:a.o b.o
gcc -o test a.o b.o
a.o : a.c
gcc -c -o a.o a.c
b.o : b.c
gcc -c -o b.o b.c
The above are three rules in Makefile. The Makefile is the file named “Makefile”. When we compile the program, just execute the make command, and it will generate the first target test executable program. If it finds that a.o or b.o does not exist, it will first generate a.o or b.o. If it finds that a.o depends on a.c, and there is a.c but no a.o, it will consider a.c is newer than a.o, thus executing the command below to generate a.o. Similarly, the processing relationship of b.o and b.c is the same.
If a.c is modified and make is executed again, the intention is to generate the first target test application. It needs to generate a.o first. If it finds that a.o depends on a.c (assuming we modified a.c), it will find that a.c is newer than a.o, and it will execute gcc -c -o a.o a.c to generate a.o file. b.o depends on b.c, and if b.c has not been modified, it will not execute gcc -c -o b.o b.c to regenerate b.o file.
Now a.o and b.o are both available, where a.o is newer than test, so it will execute gcc -o test a.o b.o to relink and obtain the test executable program. Therefore, when executing make, the following two commands will be executed:
gcc -c -o a.o a.c
gcc -o test a.o b.o
On the first execution of make, the following three commands will be executed (all three commands are executed):
gcc -c -o a.o a.c
gcc -c -o b.o b.c
gcc -o test a.o b.o
On the second execution of make, there will be the following prompt:
make: `test` is up to date.
On the second execution of make, it will check the dependencies in the Makefile file, and since the dependencies have not been updated, the target file will not be regenerated, hence the above prompt. When we modify a.c and execute make again, it will execute the following two commands:
gcc -c -o a.o a.c
gcc -o test a.o b.o
If we modify both a.c and b.c, executing make will execute the following three commands.
gcc -c -o a.o a.c
gcc -c -o b.o b.c
gcc -o test a.o b.o
The a.c file has been modified, and a.o is regenerated; b.c has been modified, and b.o is regenerated; since both a.o and b.o are updated, the test executable program is relinked. The rules of Makefile are not difficult.
The rules are the core of Makefile. When executing the make command, it will look for a file named Makefile in the current directory and execute the judgments/commands inside based on its content.
Previous article:
Pointer to char and int vs Pointer to array and string
Wei Dongshan founded Baijun Net official subscription account in 2012, publishing standard: easy to understand and practical.
To learn embedded Linux, watch Wei Dongshan’s videos.