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命 ...
随机推荐
- html的body内标签之图片及表格
<li> list 标签定义和用法: <li> 标签定义列表项目. <li> 标签可用在有序列表 (<ol>) 和无序列表 (<ul>) 中 ...
- bzoj 1862: [Zjoi2006]GameZ游戏排名系统 & bzoj 1056: [HAOI2008]排名系统
傻叉了一晚上,把t打成x,然后这题神奇在于输出一段数,不足的不用输出,一开始我的是直接找没有后面就退,然后这样会格式错误囧……然后最后zj的还卡了下空间,于是不用string就过了……string毁一 ...
- [Leetcode] Sum root to leaf numbers求根到叶节点的数字之和
Given a binary tree containing digits from0-9only, each root-to-leaf path could represent a number. ...
- 使用httpclient post请求中文乱码解决办法
使用httpclient post请求中文乱码解决办法 在使用httpclient发送post请求的时候,接收端中文乱码问题解决. 正文: 我们都知道,一般情况下使用post请求是不会出现中文乱码 ...
- [技巧篇]08.Struts2拦截器中获取Servlet API方法
讲课中遇到的解决Session拦截器的后腿问题,还有如何在拦截器中获取Servlet API,这里留一个备注,方便学生查找
- 51Nod 1031 骨牌覆盖 | Fibonacci
Input 输入N(N <= 1000) Output 输出数量 Mod 10^9 + 7 Input示例 3 Output示例 3 思路:对于第x块骨牌的情况,我们用a[x]表示其方法数:其比 ...
- 数学:BSGS
先来稍微回顾一下,我们已经会求模线性方程(包括其特殊情况乘法逆元) 我们还会进行幂取模的快速算法(模是质数用费马小定理,模一般情况用欧拉定理) 对于幂中指数特别大的情况,我们还延伸出了拓展欧拉定理来解 ...
- LightOJ 1278 - Sum of Consecutive Integers 分解奇因子 + 思维
http://www.lightoj.com/volume_showproblem.php?problem=1278 题意:问一个数n能表示成几种连续整数相加的形式 如6=1+2+3,1种. 思路:先 ...
- jedis,spring-redis-data 整合使用,版本问题异常
jedis,spring-redis-data 整合使用,版本不匹配的时候经常会报一些异常,例如1: java.lang.NoClassDefFoundError: org/springframewo ...
- 使用awk批量杀进程的命令
在做系统运维的过程中,有时候会碰到需要杀掉某一类进程的时候,如何批量杀掉这些进程,使用awk命令是很好的选择. ps -ef|grep aaa|grep -v grep|awk '{print &qu ...