Makefile系列之一 : 书写规则

摘要:
可以通过命令makeclean来删除所有编译时产生的中间文件。tab键不能出现在include前面,空格可以。GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中,为每一个“name.c”的文件都生成一个“name.d”的Makefile文件,[.d]文件中就存放对应[.c]文件的

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系列之一 : 书写规则》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇[mybatis]list的foreach的使用jmeter使用BeanShell断言下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

linux时间同步,ntpd、ntpdate

在Windwos中,系统时间的设置很简单,界面操作,通俗易懂。而且设置后,重启,关机都没关系。系统时间会自动保存在Bios的时钟里面,启动计算机的时候,系统会自动在Bios里面取硬件时间,以保证时间的不间断。 但在Linux下,默认情况下,系统时间和硬件时间,并不会自动同步。在Linux运行过程中,系统时间和硬件时间以异步的方式运行,互不干扰。硬件时间的运...

asterisk AGI编程收藏

  查看文章     asterisk AGI编程收藏   Introduction The AGI facility allows you to launch scripts, written in just about any language, from an Asterisk dial plan. Communication between...

Ubuntu 下ftp服务器的安装配置

Ubuntu 下ftp服务器的安装配置 FTP服务器是平时应用最为广泛的服务之一。VSFTP是Very Secure FTP的缩写,意指非常安全的FTP服务。VSFTP功能强大,通过结合本地系统的用户认证模块及其多功能的配置项目,可以快速有效的搭建强大的多用户FTP服务。 首先我们考虑搭建FTP服务需要关注的都有哪些方面?比如我们最经常关注的有: 1、如...

vue后台管理系统项目

项目介绍1.项目根目录文件 2.源码子目录结构 3.api目录 4.assets目录 5.components目录 6.mixins目录 7.permission目录 8.router目录 9.store目录 10.styles目录 11.utils目录 项目文件介绍1.安装element-ui组件实现按需加载 // 1.1.npm...

linux下vi命令大全

进入vi的命令vi filename :打开或新建文件,并将光标置于第一行首vi +n filename :打开文件,并将光标置于第n行首vi + filename :打开文件,并将光标置于最后一行首vi +/pattern filename:打开文件,并将光标置于第一个与pattern匹配的串处vi -r filename :在上次正用vi编辑时发生系统崩...

linux目录文件与系统启动(2)/etc系统初始化及设置相关重要文件

网卡配置文件 路径:/etc/sysconfig/network-scripts/ifcfg-ens33 ens33是你的网卡名称 图形化网络配置方式:nmtui 命令行网络配置方式:vi /etc/sysconfig/network-scripts/ifcfg-ens33 DNS配置文件:/etc/resolv.conf,基本废弃,由网卡设置里的DN...