Makefile is an essential project management file that must be mastered in the development of C/C++ programs in a Linux environment. When you use the make command to compile a project, the make tool first looks for the Makefile in the root directory of the project before it can compile the program based on this file. So, what role does the Makefile play during the compilation process? This must be understood starting from the program’s compilation and linking process.
When writing programs in Windows, you only need to know how to use an Integrated Development Environment (IDE) because the IDE integrates services for writing, compiling, linking, running, and project management. A beginner programmer can create a file, write a program, and click the run button in the menu bar to execute the program without needing to worry about how the program is compiled and run at a lower level.
In contrast, when writing programs in Linux, due to the lack of mature IDEs in the early days, various commands are generally used for compilation: source files are compiled into executable files using a compiler, assembler, and linker, and then run manually. For example, take the simplest hello world program:
// helloworld.c
#include <stdio.h>
int main(void)
{
printf("hello zhaixue.cc!\n");
return 0;
}
If you want to compile the above helloworld.c into an executable file, you generally need to go through four steps: preprocessing, compiling, assembling, and linking, with each step calling the preprocessor, compiler, assembler, and linker respectively.
In a Linux environment, if you have installed the GCC compiler, you will see various binary executable files in the installation directory:
-
cpp: Preprocessor
-
ccl: Compiler
-
as: Assembler
-
ld: Linker
-
ar: Static library creation tool
During the compilation process, these tools are used to complete each step of the program compilation. To simplify the compilation process, the GCC compiler generally provides a gcc command. When you use the gcc command to compile a program:
# gcc -o a.out helloworld.c
gcc will automatically call the preprocessor, compiler, assembler, and linker to complete the entire compilation process without requiring the user to enter each command individually.
Of course, gcc also provides a series of parameters to control the compilation process. If you only want to preprocess helloworld.c without compiling, you can use the -E parameter:
# gcc -E helloworld.c
If you want to assemble helloworld.c, you can use the following command to compile the source file helloworld.c into an assembly file helloworld.S:
# gcc -S -o helloworld.S helloworld.c
If you only want to compile the source file without linking, you can use the -c parameter to compile helloworld.c into the corresponding object file helloworld.o:
# gcc -c -o helloworld.o helloworld.c
For some simple programs, single-file programs, using gcc for compilation is very convenient. However, when a project is large and has many source files, using gcc can become cumbersome:
# gcc -o a.out helloworld.c module1.c module2.c module3.c
For example, the Linux kernel source code has tens of thousands of source files (.c, .S, and .h). If you still use gcc for compilation, you would have to input tens of thousands of source files every time, which is too tedious. At this point, the automation compilation tool make comes into play: using make to compile programs does not require entering the source files each time; just typing the make command in the command line can automate the compilation process.
Make depends on the Makefile. Therefore, before using the make command to compile a program, you need to write a Makefile:
a.out: helloworld.o
gcc -o a.out helloworld.o
helloworld.o: helloworld.c
gcc -c -o helloworld.o helloworld.c
clean:
rm -f a.out helloworld.o
After writing the Makefile, place it in the same directory as the helloworld.c source file, navigate to that directory, and simply type the make command to automatically complete the program compilation.
# ls
helloworld.c Makefile
# make
gcc -o a.out helloworld.c
# ls
a.out helloworld.c helloworld.o Makefile
# ./a.out
hello world!
# make clean
rm -f a.out helloworld.o
# ls
helloworld.c Makefile
The filename of the makefile usually has three formats: Makefile, makefile, GNUmakefile. Make will automatically look for these three filenames in the current directory. If none are found, make cannot continue the compilation and will produce an error and exit:
make: *** No targets specified and no makefile found. Stop.
The Necessity of Learning Makefile
Makefile plays a very important role in project management and the compilation and linking process of programs. It is an essential skill for development in the Linux/Unix environment, and a core skill for system architects and project managers. Mastering Makefile will give you a deeper understanding of the underlying software construction systems and the program compilation process.
By mastering the basic principles of Makefile and comparing them with IDEs in Windows, you will find that their working principles are quite similar and interconnected:
When you add source files to a project through a project manager or resource explorer window in C-Free or VC++6.0, a corresponding Makefile-like file is automatically generated at a lower level. When you click the compile or run button, the IDE will compile the program based on this Makefile. Different IDEs may have variations in the filename and format of the corresponding Makefile, such as VC++6.0 using Nmake, but the principle is the same: through Makefile, we can build the source files needed to compile an executable file a.out. Based on this dependency, the make tool can call the gcc command to compile each source file into the corresponding object file and then link them into the final executable file: a.out
The ability to write Makefile can indirectly reflect a person’s capability to complete large project engineering tasks. If you are doing C/C++ development in Linux, mastering Makefile may allow you to gain a deeper understanding of the project and take control of the entire project’s construction and maintenance.
Makefile is also a powerful tool for studying open-source projects. Many open-source projects may have incomplete documentation, and the Makefile serves as a map for the open-source project. By starting with the Makefile, you can quickly glimpse the framework and overview of the entire open-source project, allowing you to delve into the code without getting lost.
In summary, if you are a development engineer, embedded engineer, or kernel driver development engineer working in Linux, mastering Makefile is an essential skill. Like git and vim, mastering this “Linux trio” will make your work more efficient and effective.
The subsequent tutorials on Makefile have been archived on the blog: www.zhaixue.cc. Click on the “Read Original” button in the lower right corner to read all articles.