自己写一个简单通用的Makefile

来源:互联网 发布:sql语句修改字段名 编辑:程序博客网 时间:2024/06/11 09:52

转自:http://blog.csdn.net/u011913612/article/details/52102241

一.makefile的作用

          Makefile是用于自动编译和链接的,一个工程有很多文件组成,每一个文件的改变都会导致工程的重新链接,但是不是所有的文件都需要重新编译,Makefile中记录有文件的信 息,在make时会决定在链接的时候需要重新编译哪些文件。Makefile的宗旨就是:让编译器知道要编译一个文件需要依赖其他的哪些文件。当那些依赖文件有了改变,编译器会自动发现最终的生成文件已经过时,而应该重新编译相应的模块。 makefile带来的好处就是—"自动化编译",一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。默认的情况下,make命令会在当前目录下按顺序找寻文件名为"GNUmakefile"、"makefile"、"Makefile"的文件,找到了解释这个文件。当然也可以使用make -f DIR/makefile 来指定用于makefile文件

二.makefile的几点基础知识

      1.赋值符号的区别
         =  是最基本的赋值,用到了之后才赋值,不能在变量后追加内容
     := 是覆盖之前的值,立即赋值,可以在变量后追加内容
    ?= 是如果没有被赋值过就赋予等号后面的值
    += 是添加等号后面的值

2.自动变量

    $<    第一个依赖文件的名称
    $?    所有的依赖文件,以空格分开,这些依赖文件的修改日期比目    标的创建日期晚
    $@  目标的完整名称
    $^    所有的依赖文件,以空格分开,不包含重复的依赖文件

3.几个常用的函数

1. $(patsubst %.c,%.o,x.c.c bar.c)
把字串“x.c.cbar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.obar.o”
2.$(filter <pattern...>,<text> )
以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以有多个模式。 
3.$(filter-out <pattern...>,<text> )
4.$(foreach <var>,<list>,<text> )
把参数<list>中的单词逐一取出放到参数<var>所指定的变量中, 然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,循环这个过程。
5.shell函数,例如files := $(shell echo *.c)

三.通用Makefile的编译过程

       从顶层开始递归进入子目录,当进入到一个目录的最底层时,开始使用编译器编译,再将该层的所有.o文件打包成build-in.o,返回它的上一层目录再递归进入子目录,当编译完所有的子目录后,就开始编译顶层的.c文件,最后将顶层的.o文件和顶层每个子目录的build-in.o链接成我们的目标文件。

思维导图:


四、实战

假如有这样一个目录结构的工程

a

----d

----a.c

b

----b.c

c

----c.c

main.c



顶级Makefile:

CROSS_COMPILE =AS= $(CROSS_COMPILE)asLD= $(CROSS_COMPILE)ldCC= $(CROSS_COMPILE)gccCPP= $(CC) -EAR= $(CROSS_COMPILE)arNM= $(CROSS_COMPILE)nmSTRIP= $(CROSS_COMPILE)stripOBJCOPY= $(CROSS_COMPILE)objcopyOBJDUMP= $(CROSS_COMPILE)objdumpexport AS LD CC CPP AR NMexport STRIP OBJCOPY OBJDUMPCFLAGS := -Wall -O2 -gLDFLAGS := export CFLAGS LDFLAGSTOPDIR := $(shell pwd)export TOPDIRTARGET := test obj-y += main.o obj-y += a/ obj-y += b/ obj-y += c/all : make -C ./ -f $(TOPDIR)/Makefile.build$(CC) $(LDFLAGS) -o $(TARGET) built-in.oclean:rm -f $(shell find -name "*.o")rm -f $(shell find -name "*.d")rm -f $(TARGET).PHONY:all clean 


这里前面就是定义一些变量,all是工程默认的目标,它是一个伪目标,进入伪目标后执行的命令就是执行Makefile.build,这里就会引起递归调用,在Makefile.build中又会调用Makefile.build.一直到Makefile.build返回以后,会使用Makefile.build最后生成的built-in.o生成最终的目标文件。

Makefile.build:

PHONY := buildbuild :obj-y :=subdir-y :=include Makefile__subdir-y:= $(patsubst %/,%,$(filter %/, $(obj-y)))subdir-y+= $(__subdir-y)subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)cur_objs := $(filter-out %/, $(obj-y))dep_files := $(foreach f,$(cur_objs),.$(f).d)#dep_files := $(wildcard $(dep_files))#ifneq ($(dep_files),)#  include $(dep_files)#endifPHONY += $(subdir-y)build : $(subdir-y) built-in.o$(subdir-y):make -C $@ -f $(TOPDIR)/Makefile.buildbuilt-in.o : $(cur_objs) $(subdir_objs)$(LD) -r -o $@ $^dep_file = .$@.d%.o : %.c$(CC) $(CFLAGS) -Wp,-MD,$(dep_file) -c -o $@ $<.PHONY : $(PHONY)

Makefile.build会加载顶级目录下的Makefile,在顶级目录下的Makefile中已经给obj-y添加了一些条目,subdir-y就是获取子目录,然后对每一个子目录又调用Makefile.build。

当递归到没有子目录的目录时,Makefile.build开始返回,并使用$(CC)对源文件进行编译,将所有的.c生成.o文件,并将当前目录下的.o和子目录下的build-in.o连接成当前目录下的build-in.o,并回返上级目录,一次往复,最终返回到顶级目录,在顶级目录下生成build-in.o。返回到顶级目录后,Makefile.build返回到了Makefile中,Makefile在使用build-in.o生成指定的目标文件。至此,递归结束,整个系统编译完成。

这里的-MD选项和.$@.d的作用是 生成头文件的依赖关系时,把依赖关系写入到这个文件中去。

要想彻底看懂的话,建议认真推倒下每一个变量的值,比对目录与文件,会更好理清编译的过程。

子目录下的Makefile:

obj-y += a.oobj-y += d/

子目录的Makefile就是添加该目录中的文件与子目录,子目录以/结尾。

验证:

执行make

打印如下:

make -C ./ -f /home/dragon/liujinwei/uni-makefile/Makefile.buildmake[1]: Entering directory `/home/dragon/liujinwei/uni-makefile'make -C a -f /home/dragon/liujinwei/uni-makefile/Makefile.buildmake[2]: Entering directory `/home/dragon/liujinwei/uni-makefile/a'make -C d -f /home/dragon/liujinwei/uni-makefile/Makefile.buildmake[3]: Entering directory `/home/dragon/liujinwei/uni-makefile/a/d'gcc -Wall -O2 -g -Wp,-MD,.d.o.d -c -o d.o d.cld -r -o built-in.o d.omake[3]: Leaving directory `/home/dragon/liujinwei/uni-makefile/a/d'gcc -Wall -O2 -g -Wp,-MD,.a.o.d -c -o a.o a.cld -r -o built-in.o a.o d/built-in.omake[2]: Leaving directory `/home/dragon/liujinwei/uni-makefile/a'make -C b -f /home/dragon/liujinwei/uni-makefile/Makefile.buildmake[2]: Entering directory `/home/dragon/liujinwei/uni-makefile/b'gcc -Wall -O2 -g -Wp,-MD,.b.o.d -c -o b.o b.cld -r -o built-in.o b.omake[2]: Leaving directory `/home/dragon/liujinwei/uni-makefile/b'make -C c -f /home/dragon/liujinwei/uni-makefile/Makefile.buildmake[2]: Entering directory `/home/dragon/liujinwei/uni-makefile/c'gcc -Wall -O2 -g -Wp,-MD,.c.o.d -c -o c.o c.cld -r -o built-in.o c.omake[2]: Leaving directory `/home/dragon/liujinwei/uni-makefile/c'gcc -Wall -O2 -g -Wp,-MD,.main.o.d -c -o main.o main.cld -r -o built-in.o main.o a/built-in.o b/built-in.o c/built-in.omake[1]: Leaving directory `/home/dragon/liujinwei/uni-makefile'gcc  -o test built-in.o#### make completed successfully  ####


make之后的目录:

顶级:


a目录:


d目录:




0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 找不到领导电话不接短信不回怎么办 微信账号被别人手机号冻结了怎么办 微信冻结了手机号也让人换了怎么办 顺丰快递拒收退回丢件了怎么办 京东买东西快递电话没有听到怎么办 在京东购物自己电话号输错了怎么办 北京房子卖了户口没地方迁怎么办 微信弄丢了微信密码找不到了怎么办 微信背人用过找不到密码怎么办 超市的微信支付宝收付款怎么办 办理联华超市的会员储蓄卡怎么办 卡杰文具密码本如果忘记密码怎么办 火狐浏览器阻止要访问的网页怎么办 点我达被永久停用了怎么办 刚下的软件点开系统显示停用怎么办 红酒洋酒啤酒一起喝胃不舒服怎么办 儿子13岁初一不想读书了怎么办 微信不小心点了注册新账号怎么办 在京东买东西商家不发货怎么办 在京东买东西坏了商家不退货怎么办 苯扎氯铵溶液不小心喝了一口怎么办 苯扎氯铵溶液没有稀释就用了怎么办 牛油果切开了但是没熟怎么办 手机安装程序时解析包出错怎么办 因俩人不合适分手了很难受怎么办 中考结束后成绩不好的该怎么办 在京东自营药房买药没有处方怎么办 平安普惠账号不可以注销怎么办? 京东购物非自营货没到降价了怎么办 实体店商家不承认卖的是假货怎么办 衣服上的装饰圆扣掉下来了怎么办 没在京东买东西却收到退款怎么办 小米分期付款买的手机不要了怎么办 唯品会在线支付后商品有问题怎么办 红米手机把时间删了怎么办 红米桌面上的时间删了怎么办 华为手机玩游戏老是闪退怎么办 别人家无线网距离太远信号差怎么办 微信公众号交话费交错了怎么办 手机卡里还有话费销户的话怎么办 号码忘记交话费变成空号怎么办