Advanced example of Makfile application

    I recently read Chen Hao's "Writing Makefiles with Me", which introduces a lot of Makefile knowledge, but there are few examples. I will use some automated variables here to understand the powerful functions of Makefiles.
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.


Getting started:
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 main
The 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

If you only want to see the execution command of the Makefile, you can use the debugging of the Makefile, and you only need to bring one of the following parameters when executing make.
"-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

“-p”
“--print-data-base”
Output all data in the makefile, including all rules and variables. This parameter will cause a simple makefile to output a bunch of information. If you just want to output information without executing the makefile, you can use the "make -qp" command. If you want to see the default variables and rules before executing the makefile, you can use "make -p -f /dev/null". The information output by this parameter will contain the file name and line number of your makefile, so it can be useful to use this parameter to debug your makefile, especially if your environment variables are complex.

============================================


Advanced:
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




Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324755958&siteId=291194637