Makfile 应用进阶实例

本文转载自http://blog.csdn.net/li_wen01/article/details/57947242


最近看了陈皓的《跟我一起写Makefile》,里面介绍了很多的Makefile的知识,但是实例较少,我这里举例几个自动化变量使用,以便了解Makefile的强大功能。
关于自动化变量,在陈皓的文档里有下面的描述:
$@
    表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
$%
    仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。
$<
    依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
$?
    所有比目标新的依赖目标的集合。以空格分隔。
$^
    所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。
$+
    这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。
$*

   这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo"。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名,所以,"$*"的值就是"foo"。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用"$*",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么"$*"就是空值。


入门:
假如我们有下面的程序:
[python]  view plain  copy
 print ?
  1. /* test1.c */   
  2. #include                                              
  3. void test3_printf(void)          
  4. {                                
  5.     printf("I am test3 !\n");    
  6. }  
  7.   
  8.   
  9. /* test2.c */   
  10. #include                                          
  11. void test2_printf(void)        
  12. {                              
  13.     printf("I am test2 !\n");  
  14. }                              
  15.   
  16.   
  17. /* test3.c */   
  18. #include   
  19. void test1_printf(void)  
  20. {  
  21.     printf("I am test1 !\n");  
  22. }   
  23.    
  24. /* test3.h */              
  25. #ifndef _TEST_3_H          
  26. #define _TEST_3_H                                 
  27. void test3_printf(void);                           
  28. #endif     
  29.                   
  30. /* test2.h */              
  31. #ifndef _TEST_2_H          
  32. #define _TEST_2_H                                  
  33. void test2_printf(void);                           
  34. #endif       
  35.   
  36.   
  37. /* test1.h */                
  38. #ifndef _TEST_1_H   
  39. #define _TEST_1_H   
  40. void test1_printf(void);  
  41. #endif  
  42.   
  43.   
  44. /* main.c */  
  45. #include "test1.h"  
  46. #include "test2.h"  
  47. #include "test3.h"  
  48.   
  49.   
  50. void main(void)  
  51. {  
  52.     test1_printf();  
  53.     test2_printf();  
  54.     test3_printf();  
  55. }  

如果不使用Makefile 文件,我们可以用下面的命令来执行:

[python]  view plain  copy
 print ?
  1. [root@redhat Makefile]# ls  
  2. main.c  test1.c  test1.h  test2.c  test2.h  test3.c  test3.h  
  3. [root@redhat Makefile]# gcc -c test1.c  
  4. [root@redhat Makefile]# gcc -c test2.c  
  5. [root@redhat Makefile]# gcc -c test3.c  
  6. [root@redhat Makefile]# gcc -c main.c   
  7. [root@redhat Makefile]# ls  
  8. main.c  main.o  test1.c  test1.h  test1.o  test2.c  test2.h  test2.o  test3.c  test3.h  test3.o  
  9. [root@redhat Makefile]# gcc -o main main.o test1.o test2.o test3.o  
  10. [root@redhat Makefile]# ls  
  11. main  main.c  main.o  test1.c  test1.h  test1.o  test2.c  test2.h  test2.o  test3.c  test3.h  test3.o  
  12. [root@redhat Makefile]# ./main   
  13. I am test1 !  
  14. I am test2 !  
  15. I am test3 !  
gcc  的编译参数 -c  表示:只进程编译,不进行连接。-o 用来指定输出文件。gcc -o main main.o test1.o test2.o test3.o 命令表示 连接main.o test1.o test2.o test3.o 生成指定的文件main 可执行文件

如果使用Makefile 文件来做,最简单的可以是如下:
[python]  view plain  copy
 print ?
  1. main: main.o test1.o test2.o test3.o  
  2.     gcc -o main main.o test1.o test2.o test3.o  
  3. main.o:main.c test1.h test2.h test3.h  
  4.     gcc -c main.c   
  5. test1.o:test1.c test1.h  
  6.     gcc -c test1.c   
  7. test2.o:test2.c test2.h  
  8.     gcc -c test2.c   
  9. test3.o:test3.c test3.h  
  10.     gcc -c test3.c   
  11.   
  12. clean:  
  13.      rm -rf *.o main  
Makefile的一般书写方式是:
目标:依赖文件
     生成目标文件的规则
英文的表述是:
targets: prerequisites
    command
在Makefile文件中需要注意一点的就是:第一条规则中的目标将被确立为最终的目标。这里的最终目标也就是生成可执行文件main  编译运行结果如下:
[python]  view plain  copy
 print ?
  1. [root@redhat Makefile]# ls  
  2. main.c  Makefile  test1.c  test1.h  test2.c  test2.h  test3.c  test3.h  
  3. [root@redhat Makefile]# make  
  4. gcc -c main.c   
  5. gcc -c test1.c   
  6. gcc -c test2.c   
  7. gcc -c test3.c   
  8. gcc -o main main.o test1.o test2.o test3.o  
  9. [root@redhat Makefile]# ls  
  10. main  main.c  main.o  Makefile  test1.c  test1.h  test1.o  test2.c  test2.h  test2.o  test3.c  test3.h  test3.o  
  11. [root@redhat Makefile]# ./main   
  12. I am test1 !  
  13. I am test2 !  
  14. I am test3 !  

进阶:
进一步简化Makefile文件,可以有如下的写法:
[python]  view plain  copy
 print ?
  1. target = test  
  2. $(target): main.o test1.o test2.o test3.o  
  3.     gcc -o $@ $^  
  4. main.o: main.c test1.h test2.h test3.h  
  5.     gcc -c $<  
  6. test1.o: test1.c test1.h  
  7.     gcc -c $<  
  8. test2.o: test2.c test2.h  
  9.     gcc -c $<  
  10. test3.o: test3.c test3.h  
  11.     gcc -c $<  
  12. clean:  
  13.     $(RM) -f *.o $(target)  

上面的Makefile文件中同时使用了$@   $^   $<  三个自动化变量


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

Makeflie 调试

如果只想看Makefile的执行命令,可以使用Makefile的调试,只需要执行make的时候带上下面的参数之一就可以了。
“-n”
“--just-print”
“--dry-run”
“--recon”
比如上面的Makefile,如果执行 make -n  它的输出如下:
[python]  view plain  copy
 print ?
  1. licaibiao@ubuntu:~/Makefile_test$   
  2. licaibiao@ubuntu:~/Makefile_test$ ls  
  3. main.c  Makefile  test1.c  test1.h  test2.c  test2.h  test3.c  test3.h  
  4. licaibiao@ubuntu:~/Makefile_test$ make -n  
  5. gcc -c main.c  
  6. gcc -c test1.c  
  7. gcc -c test2.c  
  8. gcc -c test3.c  
  9. gcc -o test main.o test1.o test2.o test3.o  
  10. licaibiao@ubuntu:~/Makefile_test$ ls  
  11. main.c  Makefile  test1.c  test1.h  test2.c  test2.h  test3.c  test3.h  
  12. licaibiao@ubuntu:~/Makefile_test$   
  13. licaibiao@ubuntu:~/Makefile_test$   
  14. licaibiao@ubuntu:~/Makefile_test$   

可以看到,make  -n  指输出了make执行的命令,但是实际并没有进行编译工作。使用下面的参数,将输出更加多的Makefile信息

“-p”
“--print-data-base”
输出makefile中的所有数据,包括所有的规则和变量。这个参数会让一个简单的makefile都会输出一堆信息。如果你只是想输出信息而不想执行makefile,你可以使用“make -qp”命令。如果你想查看执行makefile前的预设变量和规则,你可以使用“make –p –f /dev/null”。这个参数输出的信息会包含着你的makefile文件的文件名和行号,所以,用这个参数来调试你的makefile会是很有用的,特别是当你的环境变量很复杂的时候。

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


高级:
进阶版Makefile文件可以写成下面的这样:
[python]  view plain  copy
 print ?
  1. target = test  
  2. $(target): main.o test1.o test2.o test3.o  
  3.     gcc -o $@ $^  
  4. .c .o:  
  5.     gcc -c $<  
  6. clean:  
  7.     $(RM) -f *.o $(target)  
    这里需要注意的一点是:
.c .o: 
gcc -c $< 
    这里表示的是,所有的.o文件都是依赖于相应的.c文件,这是Makefile的“ 后缀规则 ”,是一种比较古老的规则,可以使用“ 模式规则 ”来实现:
[python]  view plain  copy
 print ?
  1. target = test  
  2. $(target): main.o test1.o test2.o test3.o  
  3.     gcc -o $@ $^  
  4. %.c: %.o  
  5.     gcc -c $<  
  6. clean:  
  7. $(RM) -f *.o $(target)  
后缀规则和模式规则都属于Makefile的 隐含规则
后缀规则的Makefile执行结果如下:
[python]  view plain  copy
 print ?
  1. licaibiao@ubuntu:~/Makefile_test$ ls  
  2. main.c    Makefile1  test1.h  test2.h  test3.h  
  3. Makefile  test1.c    test2.c  test3.c  
  4. licaibiao@ubuntu:~/Makefile_test$ make   
  5. cc    -c -o main.o main.c  
  6. cc    -c -o test1.o test1.c  
  7. cc    -c -o test2.o test2.c  
  8. cc    -c -o test3.o test3.c  
  9. gcc -o test main.o test1.o test2.o test3.o  
  10. licaibiao@ubuntu:~/Makefile_test$ ls  
  11. main.c  Makefile   test     test1.h  test2.c  test2.o  test3.h  
  12. main.o  Makefile1  test1.c  test1.o  test2.h  test3.c  test3.o  
  13. licaibiao@ubuntu:~/Makefile_test$ ./test   
  14. I am test1 !  
  15. I am test2 !  
  16. I am test3 !  
  17. licaibiao@ubuntu:~/Makefile_test$   
模式规则的Makefile执行结果如下:
[python]  view plain  copy
 print ?
  1. licaibiao@ubuntu:~/Makefile_test$   
  2. licaibiao@ubuntu:~/Makefile_test$ ls  
  3. main.c    Makefile1  test1.h  test2.h  test3.h  
  4. Makefile  test1.c    test2.c  test3.c  
  5. licaibiao@ubuntu:~/Makefile_test$ make  
  6. gcc -c main.c  
  7. gcc -c test1.c  
  8. gcc -c test2.c  
  9. gcc -c test3.c  
  10. gcc -o test main.o test1.o test2.o test3.o  
  11. licaibiao@ubuntu:~/Makefile_test$ ls  
  12. main.c  Makefile   test     test1.h  test2.c  test2.o  test3.h  
  13. main.o  Makefile1  test1.c  test1.o  test2.h  test3.c  test3.o  
  14. licaibiao@ubuntu:~/Makefile_test$   
  15. licaibiao@ubuntu:~/Makefile_test$   

终极:

Makefile可以进一步简化,下面是简化后的版本:

[python]  view plain  copy
 print ?
  1. EXE := test  
  2. %.o: %.c  
  3.     $(CC) -c $< -o $@  
  4. SOURCE := $(wildcard *.c)  
  5. OBJS   := $(patsubst %.c,%.o,$(SOURCE))  
  6. $(EXE):$(OBJS)  
  7.     $(CC) -g -o ./$(EXE) $(OBJS)  
  8. clean:  
  9.     $(RM) -f *.o $(EXE)  
第一行   :=   表示前面的变量不能使用后面的变量,比如 EXE:= $(OBJS) 将会出错。

第二行   %.o: %.c     这个是模式规则

第三行  自动化变量$< 在模式规则中,表示依赖集合, $@表示目标集合

第四行  wildcard是扩展通配符,是Makefile的一个函数,这里是Makefile的函数调用。在这里是将当前目录下所有.c 结尾的文件展开

第五行  patsubst 是替换通配符,将SOURCE中以*c 结尾的名字替换为*.o  其实这里OBJ的值就是test1.o  test2.o test3.o main.o   而SOURCE的值为:test1.c   test2.c test3.c main.c

第六行  正常的Makefile书写,目标:依赖,规则,只是这里都是用变量代替了。实际执行的是:cc -g -o ./test test2.o test3.o main.o test1.o  其中-g参数表示有添加调试信息,也就是可以直接用GDB来调试生成的test 文件。

第七行  使用了伪目标clean,用来执行清除工作。

第八行  清除编译产生的中间文件。RM 的默认值是 rm -f   这里执行的是:rm -f  *.o test


本文的测试代码 可以到这里下载:Makefile应用实例


你可能感兴趣的:(makefile)