(Click the public account above to quickly follow)
Source: Vamei
Link: http://www.cnblogs.com/vamei/archive/2013/04/29/3051062.html
When compiling a large project, there are often many target files, library files, header files, and the final executable file. There are dependencies between different files. For example, when we compile using the following commands:
$gcc -c -o test.o test.c
$gcc -o helloworld test.o
The executable file helloworld depends on test.o for compilation, and test.o depends on test.c.
Dependency
When compiling a large project, we often need to call the compiler multiple times to compile the entire project step by step based on dependencies. This method is bottom-up, meaning we compile downstream files first, then upstream files.
The make tool in UNIX systems is used to automatically record and handle dependencies between files. We don’t have to type a lot of “gcc” commands; we only need to call make to complete the entire compilation process. All dependencies are recorded in a makefile text file. We only need to run make helloworld, and make will find all the necessary dependencies to compile this file from top to bottom, and then compile from bottom to top.
(make has multiple versions; this article is based on GNU make. make will automatically search for makefile, Makefile, or GNUmakefile in the current directory)
Dependencies
Basic Concepts
We use an example C language file:
#include <stdio.h>
/*
* By Vamei
* test.c for makefile demo
*/
intmain()
{
printf(“Hello world!\n”);
return0;
}
Here is a simple makefile
# helloworld is a binary file
helloworld: test.o
echo“good”
gcc –ohelloworld test.o
test.o: test.c
gcc –c –otest.otest.c
Observe the above makefile
-
Lines starting with # are comment lines
-
target: prerequisite indicates a dependency relationship, meaning the target file (target) depends on the prerequisite file (prerequisite). There can be multiple prerequisite files, separated by spaces.
-
The indented lines after the dependency indicate the operations to implement the dependency, which are normal UNIX commands. A dependency can have multiple operations.
In plain words:
-
Want helloworld? Then you must have test.o and perform the attached operations.
-
If there is no test.o, you must search for other dependencies and create test.o.
We execute
$make helloworld
to create helloworld.
make is a recursive creation process:
-
Base Case 1: If the current dependency does not specify prerequisite files, then execute the operation directly.
-
Base Case 2: If the current dependency specifies the target file, and the required prerequisite files already exist and have not changed since the last make (based on the last write time), then execute the operation of that dependency directly.
-
If the prerequisite files required by the current target file dependency do not exist, or the prerequisite files have changed, then use the prerequisite files as new target files, search for dependencies, and create the target files.
Dotted line: Dependency retrieval
The above is the core functionality of make. With this functionality, we can record all dependencies and related operations in the project and compile using make. The following content is an extension of this core content.
Macros
In make, macros (MACRO) can be used. Macros are similar to text-type variables. For example, the following CC:
CC = gcc
# helloworld is a binary file
helloworld: test.o
echo“good”
$(CC) –ohelloworld test.o
test.o: test.c
$(CC) –c –otest.otest.c
We use CC to represent “gcc”. In the makefile, we use $(CC) to call the value of the macro. make will replace $(CC) with the value (gcc) at runtime.
Shell environment variables can be directly called as macros. If a custom macro has the same name as an environment variable, make will prioritize the custom macro.
(You can use $make -e helloworld to prioritize environment variables)
Similar to C language macros, macros in makefiles can conveniently manage some fixed appearing texts and facilitate replacement operations. For example, when we use the ifort compiler in the future, we only need to change the macro definition to:
CC = ifort
and it will be done.
Internal Macros
make has internally defined macros that can be used directly. $@ contains the name of the current dependency target file, while $^ contains the current target’s prerequisite files:
CC = gcc
# helloworld is a binary file
helloworld: test.o
echo$@
$(CC) –o$@$^
test.o: test.c
$(CC) –c –o$@$^
Internal Macros Function
$* The target file name in the current dependency, excluding the suffix.
$* The prerequisite files that have changed in the current dependency
$$ The character “$”
If the target or prerequisite file is a full path, we can add D and F to extract the folder part and file name part, for example, $(@F) indicates the file name part of the target file.
Suffix Dependencies
In the makefile, use
.SUFFIXES: .c .o
to indicate that .c and .o are suffixes.
We can use suffix dependency, for example:
CC = gcc
.SUFFIXES: .c.o
.c.o:
$(CC) –c –o$@$^
#————————–
# helloworld is a binary file
helloworld: test.o
echo$@
$(CC) –o$@$^
test.o: test.c
We define .c and .o as suffixes. And have suffix dependency .c.o: . The former is the prerequisite, and the latter is the target. (Note that the order of dependencies is different from the general dependency order)
The above test.o and test.c have a dependency, but no operation. make will find that the dependency meets the .c.o suffix dependency and execute the operations following the suffix dependency.
When the project is large, suffix dependencies are very useful. Files that meet suffix dependencies often have similar operations, and we can represent these operations using suffix dependencies to avoid repetitive input.
Others
The continuation character for makefile is
makefile often defines the following dependencies:
all:
If no filename follows make, then the dependency will be executed.
clean:
Commonly used to clean up historical files.
For example:
CC = gcc
.SUFFIXES: .c.o
.c.o:
$(CC) –c –o$@$^
#————————–
all: helloworld
<ahref=“http://www.jobbole.com/members/echo”>@echo</a> “ALL”
# helloworld is a binary file
helloworld: test.o
<ahref=“http://www.jobbole.com/members/echo”>@echo</a> $@
$(CC) –o$@$^
test.o: test.c
clean:
–rm helloworld *.o
Note: The command after @ will not display the command itself. The command after – will ignore errors (like deleting non-existent files).
Summary
The core function of make is to manage compilation based on dependencies.
The other functions of make allow users to write makefiles more conveniently.
References
http://oreilly.com/linux/excerpts/9780596100292/gnu-make-utility.html
Follow “CPP Developer”
See more selected C/C++ technical articles
↓↓↓