Makefile系列之一 : 书写规则
1. 规则
target : prerequisites
command
2. example
excute 为最终生成的可执行文件。
可以通过命令 make clean来删除所有编译时产生的中间文件。
excute : main.o a.o b.o c.o d.o
cc -o excute main.o a.o b.o c.o d.o #命令必须以tab开头
main.o : comm.h main.c
cc -c main.c
a.o : comm.h a.c a.h
cc -c a.c
b.o : b.c b.h
cc -c b.c
c.o : c.h c.c comm.h
cc -c c.c
d.o : d.h d.c
cc -c d.c
clean: #无依赖文件,make不执行后续命令
rm excute a.o b.o c.o d.o
3. make 命令的执行
1)输入make命令后,make在当前目录下找名字叫“makefile”,"Makefile"的文件
2)找到后,再去找文件中的第一个目标文件,如例子中的excute.
3) 如果excute文件不存在或其后的依赖文件更新时间比它晚,则执行后面的命令,如 cc -o excute...
4) excute文件存在后,就依次找后面的依赖文件的依赖关系,直到生成所有的中间文件,最后再生成 最终目标文件excute
4. make的自动推导能力
make可以通过目标文件自动推导出部分依赖文件,因此在编写makefile时,可以省去不少篇幅。如make找到一个目标文件 a.o 能够推出的依赖文件为 a.c 及 cc -c a.c. 因此上述例子可以写成下面的样子
excute : main.o a.o b.o c.o d.o
cc -o excute main.o a.o b.o c.o d.o #命令必须以tab开头
main.o : comm.h
a.o : comm.h a.h
b.o : b.h
c.o : c.h comm.h
d.o : d.h
.PHONY : clean
clean: #无依赖文件,make不执行后续命令
rm excute a.o b.o c.o d.o
5. 引用其它的makefile
就像C里面的 include <fname> 一样,makefile 也可以引用其它makefile,其本质就是做了一次简单的文本替换而已。
tab键不能出现在include <fname> 前面,空格可以。include 语句中可以包含文件通配符,变量
如当前有文件a.mk, b.mk, 变量$var = c.mk, 则下面语句
include *.mk $var
等价于
include a.mk b.mk c.mk #仅仅做了一次文本替换
include 文件查找顺序如下:
1)当前目录
2)执行make时,如果有“-I” 或 “--include-dir”参数,则在该参数指定的目录下去寻找
3)如果目录<prefix>/include(一般是:/usr/local/bin或/usr/include)存在,则去这里面查找
include过程中,如果出现找不到文件的情况,make会生成一条warning,但不会报错,直到makefile的读取完成,再去尝试加载那些没有找到的文件,如果仍没有找到,make则报错,停止执行。如果让其忽略,可以这样写
-include <fname> #忽略include过程中出现的错误,继续执行
6. makefile文件里的依赖目标查找顺序
1)首先在当前目录下查找
2)makefile的变量VPATH
如果makefile里定义了变量VPATH,则在该变量指定的目录下查找,变量定义如下语句:
VPATH = src: ../source
它告诉makefile去src 与 ../source目录下去查找,多个目录间用:分隔。
3) make 的关键字vpath
vpath <pattern> <direc>
在direc目录下搜索符合pattern的文件
vpath <pattern>
清除符合模式<pattern>的文件的搜索目录
vpath
清除所有已设置好了的文件搜索目录
上述三种方式中<pattern>可以包含%字符, 如下面这样一句
vpath %.h ../source
表示让make在../source目录下搜索以.h为结尾的文件
7. 伪目标
伪目标也可以有依赖文件,如果伪目标相要成为默认目标时,它必须为第一个目标,如
all : target1 target2
.PHONY : all
target1 : a.o b.o
cc -o target1 a.o b.o
target2 : a.o c.o
cc -o target2 a.o c.o
这样做的好处就是只要我们输入make all这一条命令,就可以同时生成多个可执行文件。
伪目标成为依赖的例子
.PHONY : clean cleanobj cleanbak
clean : cleanobj cleanbak
rm *.hex
cleanobj :
rm *.o
cleanbak:
rm *.bak
8. 静态模式更容易定义多个目标
规则:
<targets> : <target-pattern> : <prereq-pattern>
<commond>
targets
定义了一系列的目标文件,是目标集合。
target-pattern
target的模式,即目标模式
prereq-pattern
对target-pattern进行再一次的依赖目标定义,即目标的依赖模式。
如下列几句例子:
obj = foo.o bar.o
all : $(obj)
$(obj) : %.o : %.c
$(CC) -c $(CFLAGS) $< -o $@
上例中,表示目标从$obj中取得,%.o表示要取$obj中以.o为结尾的目标,%.c表示取目标模式中的%再加上.c, 自动化变量 $< 表示所有依赖目标集, $@表示目标集。 上面规则可以展开成以下这种
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
9. 自动生成依赖文件
假设存在依赖关系如下:
main.o : main.c defs.h
但是我们怎么才能知道main.o 的文件呢?当然我们可以去查看main.c里面include的头文件,但是但一个工程很巨大时,我们也一个个去找吗?很显然,这个工作量不但巨大,而且很容易出错,更不用说之后的维护了,幸好强大的C/C++编译器提供了一个自动生成依赖关系的一个功能,即它的“-M”的选项。如输入命令:
cc -M main.c
其输出是:
main.o : main.c defs.h
注意:使用GNU的C/C++编译器,你得用“-MM”参数,不然,“-M”参数会把一些标准库的头文件也包含进来。
gcc -M main.c的输出是:
main.o: main.c defs.h /usr/include/stdio.h //很显然,它还输出了系统库头文件
gcc -MM main.c的输出则是:
main.o: main.c defs.h
即使有了编译器为我们提供的自动生成依赖关系的命令,但是我们还得知道要为哪个文件去生成依赖关系,那么有没有更好的办法来帮我们完成makefile的编写呢?
GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中,为每一个“name.c”的文件都生成一个
“name.d”的 Makefile文件,[.d]文件中就存放对应[.c]文件的依赖关系。于是,我们可以写出[.c]文件和[.d]文件的依赖关系,并让make自动更新或生成[.d]文件,并把其包含在我们的主Makefile中,这样,我们就可以自动化地生成每个文件的依
赖关系了。
这里,我们给出了一个模式规则来产生[.d]文件:
%.d: %.c
@set -e; rm -f $@; \
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
这个规则的意思是,所有的[.d]文件依赖于[.c]文件,“rm -f $@”的意思是删除所有的目标,也就是[.d]文件,第二行的意思是,为每个依赖文件“$<”,也就是[.c]文件生成依赖文件,“$@”表示模式 “%.d”文件,如果有一个C文件是name.c,那么“%”就是“name”,“$$$$”意为一个随机编号,第二行生成的文件有可能是 “name.d.12345”,第三行使用sed命令做了一个替换,关于sed命令的用法请参看相关的使用文档。第四行就是删除临时文件。
总而言之,这个模式要做的事就是在编译器生成的依赖关系中加入[.d]文件的依赖,即把依赖关系:
main.o : main.c defs.h
转成:
main.o main.d : main.c defs.h
于是,我们的[.d]文件也会自动更新了,并会自动生成了,当然,你还可以在这个[.d]文件中加入的不只是依赖关系,包括生成的命令也可一并加入,让每个[.d]文件都包含一个完整依赖的规则。一旦我们完成这个工作,接下来,我们就要把这些自动生成的规则放进我们的主Makefile中。我们可以使用Makefile的“include”命令,来引入别的Makefile文件(前面讲过),例如:
sources = foo.c bar.c
include $(sources:.c=.d)
上述语句中的“$(sources:.c=.d)”中的“.c=.d”的意思是做一个替换,把变量$(sources)所有[.c]的字串都替换成[.d], include是按次来载入文件,最先载入的[.d]文件中的目标会成为默认目标。
Makefile系列之一 : 书写规则的更多相关文章
- Linux makefile教程之书写规则三[转]
书写规则———— 规则包含两个部分,一个是依赖关系,一个是生成目标的方法.在 Makefile中,规则的顺序是很重要的,因为,Makefile中只应该有一个最终目标,其它的目标都是被这个目标所连带出来 ...
- 很详细、很移动的Linux makefile教程:介绍,总述,书写规则,书写命令,使用变量,使用条件推断,使用函数,Make 的运行,隐含规则 使用make更新函数库文件 后序
很详细.很移动的Linux makefile 教程 内容如下: Makefile 介绍 Makefile 总述 书写规则 书写命令 使用变量 使用条件推断 使用函数 make 的运行 隐含规则 使用m ...
- [转] Makefile 基础 (3) —— Makefile 书写规则
该篇文章为转载,是对原作者系列文章的总汇加上标注. 支持原创,请移步陈浩大神博客:(最原始版本) http://blog.csdn.net/haoel/article/details/2886 我转自 ...
- Makefile详解--隐含规则
Makefile详解--隐含规则(转) Makefile系列文章,这里有个前辈连续洗了一个系列来介绍,共有26篇博客文章. http://www.cppblog.com/ivenher/archive ...
- 让你提前认识软件开发(17):makefile文件的书写及应用
第1部分 又一次认识C语言 makefile文件的书写及应用 [文章摘要] makefile用于Linux下整个project的编译.对于Linux下的C/C++语言的编译是至关重要的. 本文以实际的 ...
- Linux makefile教程之隐含规则九[转]
隐含规则 ———— 在 我们使用Makefile时,有一些我们会经常使用,而且使用频率非常高的东西,比如,我们编译C/C++的源程序为中间目标文件(Unix下是[.o] 文件,Windows下是[.o ...
- Linux makefile教程之书写命令四[转]
书写命令———— 每 条规则中的命令和操作系统Shell的命令行是一致的.make会一按顺序一条一条的执行命令,每条命令的开头必须以[Tab]键开头,除非,命令是紧跟 在依赖规则后面的分号后的.在命令 ...
- Makefile编写 五 隐含规则
隐含规则———— 在我们使用Makefile时,有一些我们会经常使用,而且使用频率非常高的东西,比如,我们编译C/C++的源程序为中间目标文件(Unix下是[.o]文件,Windows下是[.obj] ...
- Makefile系列之二 : 命令
一.显示命令 echo “@”字符可以控制命令是否在屏幕上显示,如 @echo 正在编译XXX模块...... 输出: 正在编译XXX模块...... 如果没有“@"则输出 : echo ...
- Dockerfile的书写规则和指令的使用方法
Dockfile是一种被Docker程序解释的脚本,Dockerfile由一条一条的指令组成,每条指令对应Linux下面的一条命令.Docker程序将这些Dockerfile指令翻译真正的Linux命 ...
随机推荐
- P1349 广义斐波那契数列
题目描述 广义的斐波那契数列是指形如an=p*an-1+q*an-2的数列.今给定数列的两系数p和q,以及数列的最前两项a1和a2,另给出两个整数n和m,试求数列的第n项an除以m的余数. 输入输出格 ...
- 进程间通讯-2(pipe)
通过pipe 管道的方式也可以实现进程间通信. 父进程和子进程之间可以实现相互通信. from multiprocessing import Process, Pipe def f(conn): co ...
- IBatis Map时间参数文字格式不匹配!
CS. ht.Add("start_time", startTime); Map <isNotNull prepend="and" property=&q ...
- [洛谷P3878][TJOI2010]分金币
题目大意:把$n(n\leqslant30)$个数分成两组,两组个数最多相差$1$,求出两组元素差的绝对值最小使多少 题解:模拟退火 卡点:$\exp$中的两个数相减写反,导致$\exp(x)$中的$ ...
- bzoj 1862: [Zjoi2006]GameZ游戏排名系统 & bzoj 1056: [HAOI2008]排名系统
傻叉了一晚上,把t打成x,然后这题神奇在于输出一段数,不足的不用输出,一开始我的是直接找没有后面就退,然后这样会格式错误囧……然后最后zj的还卡了下空间,于是不用string就过了……string毁一 ...
- [TJOI2013]最长上升子序列 平衡树
其实是一道性质题. 首先观察到插入的数是递增的, 那么根据上升子序列的性质, 我们的非法情况就是统计到了在一个数前面的后插入的数, 但是由于插入的数是递增的,显然插入这个数后,这个数就是最大的,所以除 ...
- Linux(ubuntu 12.04桌面版) 搭建Android开发环境
因为一些工作上的原因,需要切换到Linux环境下做点开发,我选择的Linux发行版本为ubuntu(我不建议使用fedora,我最开始就是使用的fedora,但发现并不是特别好使,有些插件没办法安装, ...
- WizTools.org RESTClient 启动方法
关于 WizTools.org RESTClient的使用 今天分享一个很好用的测试service的工具,很好用 提供两种方法使用这个东东. 第一种方法 通过cmd命令窗口. (1)cd C:\Use ...
- OpenCV学习笔记(01)我的第一个OpenCV程序(环境配置)
昨天刚刚考完编译原理,私心想着可以做一些与考试无关的东西了.一直想做和图像处理相关的东西,趁这段时间有空学习一下OpenCV,搭建环境真是一件麻烦的事情,搞了近三个小时终于OK了.先来张图: 大致描述 ...
- VC使用sqlite
SQLite可以到官方站点(http://www.sqlite.org/download.html)下载:Linux,Mac OS X, Windows下的已编译文件以及源代码.帮助文档. SQLit ...