编写通用的makefile(自己有点忘记了)

转载于: http://blog.csdn.net/ymangu666/article/details/23028747 

 

近期在看韦东山的数码相框视频,有讲到怎么写一个通用的makefile,遂赶紧记录下来。废话不多说,下面进入正题。

1)程序的编译过程
        4个步骤:预处理,编译,汇编,链接.
      预处理:  检测语法错误,把头文件包含进来,宏展开.
      编译:把.  c文件转换成汇编文件.s
     汇编:把.  s文件转换成二进制文件.o
     链接:.      .o文件+库文件 =可执行程序

    gcc -v -o hello hello.c  就可观看到编译的细节.

2)写Makefile

 

 核心:规则
 目标:依赖1 依赖2
 命令 
 命令执行的条件:
 i. "依赖"文件 比 "目标"文件 新
 ii.没有"目标"这个文件

 

 

 假设我们包含的文件要用到:a.c b.c a.h
1>  来看第一个makefile:

 

[cpp] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. test:a.c b.c a.h  
  2.     gcc -o test a.c b.c  
[html] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. </pre>对于a.c: 预处理、编译、汇编对于b.c:预处理、编译、汇编最后链接优点:命令简单缺点:如果文件很多,即使你只修改了一个文件,但是所有的文件文件都要重新"预处理、编译、汇编"      效率低<p>2>第二个Makefile</p><p></p><pre code_snippet_id="277584" snippet_file_name="blog_20140406_1_3955355" name="code" class="cpp">test:a.o b.o  
  2.     gcc -o test a.o b.o  
  3. a.o : a.c   
  4.     gcc -c -o a.o a.c     
  5. b.o : b.c  
  6.     gcc -c -o b.o b.c     

缺点:不通用,文件量大时,不方便。

 

 

3>第三个Makefile

[cpp] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. test:a.o b.o  
  2.     gcc -o test a.o b.o  
  3. a.o:a.c a.h  
  4. %.o : %.c   
  5.     gcc -c -o $@ $<  

#缺点: a.o:a.c a.h  要手动输入,文件太多就麻烦

 

 

gcc -c -o a.o a.c    //-c 把预处理,编译,汇编 做完  链接不做.

 %.o : %.c     // %通配符,%.o 表示所有以.o结尾的文件
    “$<”表示第一个依赖文件
    $^   所有的依赖目标的集合。
  “$@”表示所有的目标集
      $?   所有比目标新的依赖目标的集合
$%  仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。

4> 第四个Makefile

 

[cpp] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. objs := a.o b.o  
  2. test:$(objs)  
  3.     gcc -o test $^  
  4. # .a.o.d .b.o.d  
  5. dep_files := $(foreach f,$(objs),.$(f).d)     #让objs中的元素加'.' 前缀及'.d' 后缀  
  6. dep_files := $(wildcard $(dep_files))        #wildcard    如果里面的元素存在,则 取出  
  7.   
  8. ifneq ($(dep_files),)    #如果元素不为空  
  9.   include $(dep_files)   #把元素包含进来  
  10. endif  
  11. %.o : %.c   
  12.     gcc -Wp,-MD,.$@.d -c -o $@ $<  
  13. clean:  
  14.     rm *.o test  

 3) gcc 自动生成依赖文件
文件太多时,手动编辑哪个目标依赖哪个文件太麻烦,怎么自动生成呢?
在终端中执行命令: gcc -c -o a.o a.c -Wp,-MD,a.d
自动生在依赖文件a.d .  查看a.d中的内容:

 

可以看到 目标a.o 所依赖的所有关系,a.c a.h 及各个目录下的头文件等.

4) 编写工程的通用Makefile

本程序的Makefile分为3类:
1. 顶层目录的Makefile
2. 顶层目录的Makefile.build
3. 各级子目录的Makefile

一、各级子目录的Makefile:
   它最简单,形式如下:
obj-y += file.o
obj-y += subdir/
   
   "obj-y += file.o"表示把当前目录下的file.c编进程序里,
   "obj-y += subdir/"表示要进入subdir这个子目录下去寻找文件来编进程序里,是哪些文件由subdir目录下的Makefile决定。

注意: "subdir/"中的斜杠"/"不可省略

二、顶层目录的Makefile:
   它除了定义obj-y来指定根目录下要编进程序去的文件、子目录外,主要是定义工具链、编译参数、链接参数──就是文件中用export导出的各变量。

[cpp] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. CROSS_COMPILE =                     #如果要交叉编译的话,就是:CROSS_COMPILE = arm-linux-  
  2. AS      = $(CROSS_COMPILE)as  
  3. LD      = $(CROSS_COMPILE)ld  
  4. CC      = $(CROSS_COMPILE)gcc  
  5. CPP     = $(CC) -E  
  6. AR      = $(CROSS_COMPILE)ar  
  7. NM      = $(CROSS_COMPILE)nm  
  8.   
  9. STRIP       = $(CROSS_COMPILE)strip  
  10. OBJCOPY     = $(CROSS_COMPILE)objcopy  
  11. OBJDUMP     = $(CROSS_COMPILE)objdump  
  12.   
  13. export AS  LD CC CPP  AR  NM        #把上面定义的变量用export导出来,后面的子目录就能用.  
  14. export STRIP OBJCOPY OBJDUMP  
  15.   
  16. CFLAGS := -Wall -O2 -g    
  17. CFLAGS += -I $(shell pwd)/include -I /usr/include/freetype2      # -I 指定到哪个目录去搜索头文件。  
  18. LDFLAGS := -lm -lfreetype -lvga -lvgagl        #-lm 添加链接库。  
  19.   
  20. export CFLAGS LDFLAGS      #把这两个也导出来。  
  21. TOPDIR := $(shell pwd)         #顶层目录  
  22. export TOPDIR  
  23.   
  24. TARGET := show_file         #目标  
  25. obj-y += main.o  
  26. obj-y += display/  
  27. obj-y += draw/  
  28. obj-y += encoding/  
  29. obj-y += fonts/  
  30. all :   
  31.     make -C ./ -f $(TOPDIR)/Makefile.build  
  32.     $(CC) $(LDFLAGS) -o $(TARGET) built-in.o  
  33. clean:  
  34.     rm -f $(shell find -name "*.o")  
  35.     rm -f $(TARGET)  
  36.   
  37. distclean:  
  38.     rm -f $(shell find -name "*.o")  
  39.     rm -f $(shell find -name "*.d")  
  40.     rm -f $(TARGET)  

# -Wall 所有的警告信息  -O2 优化选项 -g 加上调试信息  用‘:’而不用‘=’号,有什么讲究?
                        #用‘=’是临时变量,只有用到时才扩展,最大的缺点是不能在变量后追加内容。            #‘:’为立即变量,马上就扩展,定下来


我们这个工程,顶层目录下 有main.c,还有display、draw等子目录。
display子目录下又有disp_manager.c fb.c文件及 test子目录。
执行Makefile时是怎么编译的呢?

 

Makefile是先从子目录开始执行的:
1>执行顶层目录Makefil程序时,先进入顶层目录,发现有这些文件和目录.将会分别进入display/  draw/ 等子目录.
2>进入子目录后,发现又有子目录的Makefil,如display/ 又发现有test/ 子目录,进入test/ 子目录,执行子目录的Makefil. 
3>把test.c  编译成test.o . 把test目录下所有的.o 文件把包成test/build_in.o
4>回到上层目录,disp_manager.c 编译成disp_manager.o  fb.c编译成fb.o .然后把disp_manager.o fb.o 及 test/build-in.o 把包成 build-in.o
 5>进入draw/ ,把draw.c 编译成draw.o  打包成build-in.o
.........
6)经过多个子目录后,回到顶层目录.把main.c 编译成main.o ,并与各个子目录的build-in.o打包成最终的build-in.o.
make -C ./ -f  Makefile.build   
-C ./  进入当前目录, -f 用哪个Makefile.build .现在是当前目录的.

三、顶层目录的Makefile.build:

   这是最复杂的部分,它的功能就是把某个目录及它的所有子目录中、需要编进程序去的文件都编译出来,打包为built-in.o
   详细的讲解请看视频。

[cpp] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. PHONY := __build       #假目标  
  2. __build:  
  3.   
  4. obj-y :=  
  5. subdir-y :=  
  6. include Makefile                #包含当前目录的makefile,才知道目标是哪些目标和目录   
  7.   
  8. # obj-y := a.o b.o c/ d/  
  9. # $(filter %/, $(obj-y))   : c/ d/  
  10. # __subdir-y  : c d  
  11. # subdir-y    : c d  
  12. __subdir-y  := $(patsubst %/,%,$(filter %/, $(obj-y)))    
  13. subdir-y    += $(__subdir-y)  
  14.   
  15. # c/built-in.o d/built-in.o  
  16. subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)  
  17.   
  18. # a.o b.o  
  19. cur_objs := $(filter-out %/, $(obj-y))  
  20.   dep_files := $(foreach f,$(cur_objs),.$(f).d)   #(foreach var,list,text) 结果是由空格隔开的‘text’ 在‘list’中多次扩展的字组成的新的‘list’  
  21.   
  22. dep_files := $(wildcard $(dep_files))  
  23.   
  24. ifneq ($(dep_files),)  
  25.   include $(dep_files)  
  26. endif  
  27.   
  28.   
  29. PHONY += $(subdir-y)  
  30.   
  31.   
  32. __build : $(subdir-y) built-in.o  
  33.   
  34. $(subdir-y):  
  35.     make -C $@ -f $(TOPDIR)/Makefile.build  
  36.   
  37. built-in.o : $(cur_objs) $(subdir_objs)  
  38.     $(LD) -r -o $@ $^            #打包  
  39.   
  40. dep_file = .$@.d  
  41.   
  42. %.o : %.c  
  43.     $(CC) $(CFLAGS) -Wp,-MD,$(dep_file) -c -o $@ $<  
  44.       
  45. .PHONY : $(PHONY)  

 #$(filter pattern...,text)返回在‘text’中由空格隔开且匹配格式‘pattern...’的字,对于不符合格式‘pattern...’的字移出。格式用‘%’写出
#$(patsubst pattern,replacement,text)  寻找‘text’中符合格式‘pattern’的字,用‘replacement’替换它们。


四、怎么使用这套Makefile:
1.把顶层Makefile, Makefile.build放入程序的顶层目录
2.修改顶层Makefile
2.1 修改工具链
2.2 修改编译选项、链接选项
2.3 修改obj-y决定顶层目录下哪些文件、哪些子目录被编进程序
2.4 修改TARGET,这是用来指定编译出来的程序的名字


3. 在各一个子目录下都建一个Makefile,形式为:
obj-y += file1.o
obj-y += file2.o
obj-y += subdir1/
obj-y += subdir2/

4. 执行"make"来编译,执行"make clean"来清除,执行"make distclean"来彻底清除

posted on 2016-12-20 12:36  Red_Point  阅读(612)  评论(0)    收藏  举报

导航