Regarding automation variables, there is the following description in Chen Hao's document:
$@
represents the target file set in the rule. In a pattern rule, if there are multiple targets, then "$@" is the set that matches the pattern definition in the target.
$%
only if the target is in the function library file, it means the target member name in the rule. For example, if a target is "foo.a(bar.o)", then "$%" is "bar.o" and "$@" is "foo.a". If the target is not a function library file ([.a] under Unix, [.lib] under Windows), then its value is empty.
$<
depends on the first target name in the target. If the dependency target is defined with a pattern (ie "%"), then "$<" will be a sequence of filesets conforming to the pattern. Note that it is taken out one by one.
$?
A collection of all dependent targets newer than target. separated by spaces.
$^
A collection of all dependent targets. separated by spaces. If there are multiple duplicates in the dependency target, this variable will remove the duplicate dependency target and keep only one copy.
$+
This variable is very similar to "$^" and is also the set of all dependent targets. It's just that it doesn't remove duplicate dependent targets.
$*
This variable represents "%" and the part before it in the target pattern. If the target is "dir/a.foo.b", and the pattern of the target is "a.%.b", then the value of "$*" is "dir/a.foo". This variable is relatively useful for constructing associated filenames. If there is no pattern definition in the target, then "$*" cannot be deduced, but if the suffix of the target file is recognized by make, then "$*" is the part except the suffix. For example: if the target is "foo.c", since ".c" is a suffix recognized by make, the value of "$*" is "foo". This feature is in GNU make and is likely not compatible with other versions of make, so you should try to avoid using "$*", except in implicit rules or static mode. If the suffix in the target is not recognized by make, then "$*" is empty.
Suppose we have the following program:
/* test1.c */ #include <stdio.h> void test3_printf(void) { printf("I am test3 !\n"); } /* test2.c */ #include <stdio.h> void test2_printf(void) { printf("I am test2 !\n"); } /* test3.c */ #include <stdio.h> void test1_printf(void) { printf("I am test1 !\n"); } / * test3.h * / #ifndef _TEST_3_H #define _TEST_3_H void test3_printf(void); #endif /* test2.h */ #ifndef _TEST_2_H #define _TEST_2_H void test2_printf(void); #endif / * test1.h * / #ifndef _TEST_1_H #define _TEST_1_H void test1_printf(void); #endif /* main.c */ #include "test1.h" #include "test2.h" #include "test3.h" void main(void) { test1_printf(); test2_printf(); test3_printf(); }
If we don't use Makefile, we can execute it with the following command:
[root@redhat Makefile]# ls main.c test1.c test1.h test2.c test2.h test3.c test3.h [root@redhat Makefile]# gcc -c test1.c [root@redhat Makefile]# gcc -c test2.c [root@redhat Makefile]# gcc -c test3.c [root@redhat Makefile]# gcc -c main.c [root@redhat Makefile]# ls main.c main.o test1.c test1.h test1.o test2.c test2.h test2.o test3.c test3.h test3.o [root@redhat Makefile]# gcc -o main main.o test1.o test2.o test3.o [root@redhat Makefile]# ls main main.c main.o test1.c test1.h test1.o test2.c test2.h test2.o test3.c test3.h test3.o [root@redhat Makefile]# ./main I am test1 ! I am test2 ! I am test3 !The compilation parameter -c of gcc means: only process compilation, no linking. -o is used to specify the output file. gcc -o main main.o test1.o test2.o test3.o command means to connect main.o
test1.o is as follows:
main: main.o test1.o test2.o test3.o gcc -o main main.o test1.o test2.o test3.o main.o:main.c test1.h test2.h test3.h gcc -c main.c test1.o: test1.c test1.h gcc -c test1.c test2.o: test2.c test2.h gcc -c test2.c test3.o: test3.c test3.h gcc -c test3.c clean: rm -rf *.o mainThe general writing method of Makefile is:
target:
the rules for generating target files depending on the file. The
English expression is:
targets: prerequisites
command
One thing to pay attention to in the Makefile file is: the target in the first rule will be established as the final target. The ultimate goal here is to generate the executable file main The result of compilation and running is as follows:
[root@redhat Makefile]# ls main.c Makefile test1.c test1.h test2.c test2.h test3.c test3.h [root@redhat Makefile]# make gcc -c main.c gcc -c test1.c gcc -c test2.c gcc -c test3.c gcc -o main main.o test1.o test2.o test3.o [root@redhat Makefile]# ls main main.c main.o Makefile test1.c test1.h test1.o test2.c test2.h test2.o test3.c test3.h test3.o [root@redhat Makefile]# ./main I am test1 ! I am test2 ! I am test3 !
Advanced: To
further simplify the Makefile, it can be written as follows:
target = test $ (target): main.o test1.o test2.o test3.o gcc -o $@ $^ main.o: main.c test1.h test2.h test3.h gcc -c $< test1.o: test1.c test1.h gcc -c $< test2.o: test2.c test2.h gcc -c $< test3.o: test3.c test3.h gcc -c $< clean: $(RM) -f *.o $(target)
The above Makefile uses both $@ $^ $< three automation variables
================================================
Makeflie debugging
"-n"
"--just-print"
"--dry-run"
"--recon"
Such as the above Makefile, if you execute make -n, the output is as follows:
licaibiao@ubuntu:~/Makefile_test$ licaibiao@ubuntu:~/Makefile_test$ ls main.c Makefile test1.c test1.h test2.c test2.h test3.c test3.h licaibiao@ubuntu:~/Makefile_test$ make -n gcc -c main.c gcc -c test1.c gcc -c test2.c gcc -c test3.c gcc -o test main.o test1.o test2.o test3.o licaibiao@ubuntu:~/Makefile_test$ ls main.c Makefile test1.c test1.h test2.c test2.h test3.c test3.h licaibiao@ubuntu:~/Makefile_test$ licaibiao@ubuntu:~/Makefile_test$ licaibiao@ubuntu:~/Makefile_test$
It can be seen that make -n refers to the output of the command executed by make, but does not actually compile. Using the following parameters will output more Makefile information
============================================
Advanced Makefiles can be written as follows:
target = test $ (target): main.o test1.o test2.o test3.o gcc -o $@ $^ .c .o: gcc -c $< clean: $(RM) -f *.o $(target)One thing to note here is:
.c .o:
gcc -c $<
This means that all .o files depend on the corresponding .c files, which is the " suffix rule " of Makefile, which is a comparison Old rules, can be implemented using " pattern rules ":
target = test $ (target): main.o test1.o test2.o test3.o gcc -o $@ $^ %.c: %.o gcc -c $< clean: $(RM) -f *.o $(target)Both suffix rules and pattern rules belong to Makefile's implicit
rules . The Makefile execution results of the suffix rules are as follows:
licaibiao@ubuntu:~/Makefile_test$ ls main.c Makefile1 test1.h test2.h test3.h Makefile test1.c test2.c test3.c licaibiao@ubuntu:~/Makefile_test$ make cc -c -o main.o main.c cc -c -o test1.o test1.c cc -c -o test2.o test2.c cc -c -o test3.o test3.c gcc -o test main.o test1.o test2.o test3.o licaibiao@ubuntu:~/Makefile_test$ ls main.c Makefile test test1.h test2.c test2.o test3.h main.o Makefile1 test1.c test1.o test2.h test3.c test3.o licaibiao@ubuntu:~/Makefile_test$ ./test I am test1 ! I am test2 ! I am test3 ! licaibiao@ubuntu:~/Makefile_test$The Makefile execution result of the pattern rule is as follows:
licaibiao@ubuntu:~/Makefile_test$ licaibiao@ubuntu:~/Makefile_test$ ls main.c Makefile1 test1.h test2.h test3.h Makefile test1.c test2.c test3.c licaibiao@ubuntu:~/Makefile_test$ make gcc -c main.c gcc -c test1.c gcc -c test2.c gcc -c test3.c gcc -o test main.o test1.o test2.o test3.o licaibiao@ubuntu:~/Makefile_test$ ls main.c Makefile test test1.h test2.c test2.o test3.h main.o Makefile1 test1.c test1.o test2.h test3.c test3.o licaibiao@ubuntu:~/Makefile_test$ licaibiao@ubuntu:~/Makefile_test$
ultimate:
The Makefile can be further simplified, here is the simplified version:
EXE := test %.o: %.c $(CC) -c $< -o $@ SOURCE := $(wildcard *.c) OBJS: = $ (patsubst% .c,% .o, $ (SOURCE)) $ (EXE): $ (OBJS) $(CC) -g -o ./$(EXE) $(OBJS) clean: $ (RM) -f * .o $ (EXE)The first line := means that the preceding variable cannot use the following variable, such as EXE:= $(OBJS) will make an error.
The second line %.o: %.c This is the pattern rule
The third line of the automation variable $< in the pattern rule, represents the set of dependencies, and $@ represents the set of targets
The fourth line wildcard is an extended wildcard, a function of Makefile, here is the function call of Makefile. Here is to expand all files ending in .c in the current directory
The fifth line patsubst is to replace the wildcard, replace the name ending with *c in SOURCE with *.o In fact, the value of OBJ here is test1.o test2.o test3.o main.o and the value of SOURCE: test1.c test2. c test3.c main.c
The sixth line of normal Makefile writing, target: dependencies, rules, but here are replaced by variables. The actual execution is: cc -g -o ./test test2.o test3.o main.o test1.o The -g parameter indicates that debugging information is added, that is, the generated test file can be directly debugged with GDB.
The seventh line uses the pseudo-target clean to perform the cleanup.
The eighth line clears the intermediate files generated by the compilation. The default value of RM is rm -f Here is the execution: rm -f *.o test
The test code of this article can be downloaded here: Makefile application example