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系列之一 : 书写规则的更多相关文章

  1. Linux makefile教程之书写规则三[转]

    书写规则———— 规则包含两个部分,一个是依赖关系,一个是生成目标的方法.在 Makefile中,规则的顺序是很重要的,因为,Makefile中只应该有一个最终目标,其它的目标都是被这个目标所连带出来 ...

  2. 很详细、很移动的Linux makefile教程:介绍,总述,书写规则,书写命令,使用变量,使用条件推断,使用函数,Make 的运行,隐含规则 使用make更新函数库文件 后序

    很详细.很移动的Linux makefile 教程 内容如下: Makefile 介绍 Makefile 总述 书写规则 书写命令 使用变量 使用条件推断 使用函数 make 的运行 隐含规则 使用m ...

  3. [转] Makefile 基础 (3) —— Makefile 书写规则

    该篇文章为转载,是对原作者系列文章的总汇加上标注. 支持原创,请移步陈浩大神博客:(最原始版本) http://blog.csdn.net/haoel/article/details/2886 我转自 ...

  4. Makefile详解--隐含规则

    Makefile详解--隐含规则(转) Makefile系列文章,这里有个前辈连续洗了一个系列来介绍,共有26篇博客文章. http://www.cppblog.com/ivenher/archive ...

  5. 让你提前认识软件开发(17):makefile文件的书写及应用

    第1部分 又一次认识C语言 makefile文件的书写及应用 [文章摘要] makefile用于Linux下整个project的编译.对于Linux下的C/C++语言的编译是至关重要的. 本文以实际的 ...

  6. Linux makefile教程之隐含规则九[转]

    隐含规则 ———— 在 我们使用Makefile时,有一些我们会经常使用,而且使用频率非常高的东西,比如,我们编译C/C++的源程序为中间目标文件(Unix下是[.o] 文件,Windows下是[.o ...

  7. Linux makefile教程之书写命令四[转]

    书写命令———— 每 条规则中的命令和操作系统Shell的命令行是一致的.make会一按顺序一条一条的执行命令,每条命令的开头必须以[Tab]键开头,除非,命令是紧跟 在依赖规则后面的分号后的.在命令 ...

  8. Makefile编写 五 隐含规则

    隐含规则———— 在我们使用Makefile时,有一些我们会经常使用,而且使用频率非常高的东西,比如,我们编译C/C++的源程序为中间目标文件(Unix下是[.o]文件,Windows下是[.obj] ...

  9. Makefile系列之二 : 命令

    一.显示命令 echo “@”字符可以控制命令是否在屏幕上显示,如 @echo 正在编译XXX模块......  输出: 正在编译XXX模块...... 如果没有“@"则输出 : echo ...

  10. Dockerfile的书写规则和指令的使用方法

    Dockfile是一种被Docker程序解释的脚本,Dockerfile由一条一条的指令组成,每条指令对应Linux下面的一条命令.Docker程序将这些Dockerfile指令翻译真正的Linux命 ...

随机推荐

  1. CF546E Soldier and Traveling

    题目描述 In the country there are n n n cities and m m m bidirectional roads between them. Each city has ...

  2. CF#67 75d Big Maximum Sum

    ~~~题面~~~ 题解: 观察到拼接后的数据范围过大,无法O(n)解决,但是大区间是由很多小区间组成,而小区间是固定的,不会变化,所以可以考虑预处理出每个小区间的信息,然后根据给定序列按顺序一步一步合 ...

  3. BZOJ4815 [CQOI2017]小Q的表格 【数论 + 分块】

    题目链接 BZOJ4815 题解 根据题中的式子,手玩一下发现和\(gcd\)很像 化一下式子: \[ \begin{aligned} bf(a,a + b) &= (a + b)f(a,b) ...

  4. 在 C Level 用 dlopen 使用 第三方的 Shared Library (.so)

    http://falldog7.blogspot.com/2013/10/android-c-level-dlopen-shared-library-so.html 在 Android 裡,撰寫 JN ...

  5. ACE反应器(Reactor)模式(4)

    转载于:http://www.cnblogs.com/TianFang/archive/2006/12/18/596012.html 定时器的实现 通过Reactor机制,还可以很容易的实现定时器的功 ...

  6. bzoj 2457 [BeiJing2011]双端队列 模拟+贪心

    [BeiJing2011]双端队列 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 457  Solved: 203[Submit][Status][D ...

  7. sql获取当前时间

    sql读取系统日期和时间的方法如下:--获取当前日期(如:yyyymmdd) select CONVERT (nvarchar(12),GETDATE(),112) --获取当前日期(如:yyyymm ...

  8. 代码Review发现问题

    FrmMain.cs中存在问题 1. int i=0 设定为了全局常量且未在类顶部,出现问题时不好查找 i 属于常用临时变量,设定全局变量容易引起混乱 2.定义的全局变量但仅在一处方法中使用,定义全局 ...

  9. ZooKeeper JMX(十一)

    JMX ZooKeeper对JMX有额外的支持,允许你查看和管理Zk群集. 这个文档假设你对JMX有基本的了解.参考Sun JMX Technology来对JMX进行入门. 关于安装一个本地和远端管理 ...

  10. ASP.NET和ASP的区别是什么

    分析: ASP与ASP.NET是Microsoft公司在Web应用程序开发上的两项重要技术. ASP与ASP.NET区别如下: (1)开发语言不同:ASP的开发语言仅局限于使用non-type脚本语言 ...