Makefile

1.问题的引入

当我们要编译成千上万个源程序文件的时候,光靠手工使用GCC工具来达到目的也许就会很没有效率,我们亟需一款能够帮助我们自动检查文件的更新情况,自动进行编译的软件,GNUmake就是这样的一款软件。
在 Linux(unix)环境下使用 GNU 的 make工具能够比较容易的构建一个属于你自己的工程,整个工程的编译只需要一个命令就可以完成编译、连接以至于最后的执行。不过这需要我们投入一些时间去完成一个或者多个称之为 Makefile 文件的编写。

2.make与Makefile的概念

2.1工程管理器make的概念

协助我们有序地、正确地自动编译整个工程的所有该编译的文件,这样的软件被称为 工程管理器,make 就是一款工程管理器软件。

2.2Makefile的概念

make 正常工作时,会读取一个称为 Makefile 的配置文件,该配置文件可以为 make 指明细致的工作规则,比如所使用的工具链、要编译的目标文件名称、要递归编译的子文件夹路径等等。对工程管理器软件的学习,主要就是对其配置文件 Makefile 的语法的学习。
Makefile 文件描述了整个工程的编译、连接等规则。其中包括:工程中的哪些源文件需要编译以及如何编译、需要创建那些库文件以及如何创建这些库文件、如何最后产生我们想要得可执行文件。尽管看起来可能是很复杂的事情,但是为工程编写 Makefile 的好处是能够使用一行命令来完成“自动化编译”,一旦提供一个(通常对于一个工程来说会是多个)正确的 Makefile。编译整个工程你所要做的唯一的一件事就是在 shell 提示符下输入 make 命令。整个工程完全自动编译,极大提高了效率。
make 是一个命令工具,它解释 Makefile 中的指令(应该说是规则)。在 Makefile 文件中描述了整个工程所有文件的编译顺序、编译规则。

2.3Makefile文件的位置

Makefile 是用来指导make对源代码进行编译的,因此在一个多目录结构的工程项目中,凡是有源码出现的目录,都会有一个 Makefile 去管理,而所有的 Makefile,都通过工程项目顶层目录下的 Makefile 去直接或简洁调用。

3.目标与依赖

目标和依赖是 Makefile 语法中最基本的概念,假设有一个源文件 a.c,编译生成 a.o ,那么前者是依赖,后者 a.o 是目标,但进一步将 a.o 编译成可执行文件 a,那么 a.o 此时就变成依赖,最终的文件 a 是目标,因此目标和依赖是相对的概念。
在这里插入图片描述
在 Makefile 中,使用冒号来区隔它们:

# 目标:依赖
a.o:a.c

# 目标:依赖列表
image:a.o b.o c.o d.o

目标:通常是最后需要生成的文件名或者为了实现这个目的而必需的中间过程文件名。可以是.o 文件、也可以是最后的可执行程序的文件名等。另外,目标也可以是一个 make执行的动作的名称,如目标“clean”,我们称这样的目标是“伪目标”。
依赖:生成规则目标所需要的文件名列表。通常一个目标依赖于一个或者多个文件。

4.规则

在目标与依赖下面,使用一种特殊的语法 “语句” 来构成一个规则,比如:

# 一套规则:
a.o:a.c 
    gcc a.c -o a.o -c -fPIC  # 行首必须是制表符tab

请注意:在上述语句中,目标与依赖、命令共同构成了一个规则,命令的行首必须是制表符 tab 键,不能是空格,否则会报错。另外,命令可以是多行:

image:a.o b.o c.o d.o 
    gcc a.c -o a.o -c -fPIC
    gcc b.c -o b.o -c -fPIC
    gcc c.c -o c.o -c -fPIC
    gcc d.c -o d.o -c -fPIC
    gcc a.o b.o c.o d.o -o image

重点:规则中的各个命令什么时候被执行?
当目标文件不存在时。
当目标文件存在,但时间戳比依赖列表中的某一文件旧时。
因此,当目标文件已经被编译且其依赖文件没有修改,那么再次执行make就不会触发任何动作,这就是make和 Makefile 的最基本的逻辑:只在有需要的时候编译,尽量提高编译效率。

5.终极目标和多目标编译

在一个 Makefile 中,可以有多套规则,也就说可以有多个目标,在这多个目标中,最先出现的被称为终极目标,它是执行make时默认的目标,比如:

a:a.c
    gcc a.c -o a
b:b.c
    gcc b.c -o b

以上 Makefile 中,a是终极目标,b不是,因此直接执行make时,只会针对第一套规则进行推导:

gec@ubuntu:~$ ls
a.c b.c Makefile
gec@ubuntu:~$ make
gcc a.c -o a

要执行第二套规则,则需要在执行make命令时特意指定,比如:

gec@ubuntu:~$ make b
gcc b.c -o b

对于这种多目标编译,更传统的做法是,虚构一个被大家共同依赖的伪目标,利用 Makefile 编译链自动编译所有的目标,比如:

all:a b

a:a.c
    gcc a.c -o a

b:b.c
    gcc b.c -o b

执行结果是:

gec@ubuntu:~$ make
gcc a.c -o a
gcc b.c -o b

6.隐式规则

Makefile 会根据目标和依赖简单地自动推导出编译语句,这种情况叫隐式规则,比如:

all:a b

在上述 Makefile 中,没有任何编译语句,甚至连a和b的依赖文件都没写,但这个 Makefile 可以正常执行:

gec@ubuntu:~$ make
cc a.c -o a
cc b.c -o b

此时,Makefile 的执行逻辑是:监测到终极目标的依赖文件a和b不存在,就会自动寻找以a和b为目标的规则,在本文件中没有,然后就会尝试在本目录中寻找 a.c 和 b.c ,如果找到了就以它们为依赖文件,自动编译它们,这个过程就是隐式规则。
注意到,隐式规则可以帮忙处理一些比较简单地编译,它要求目标文件和依赖文件同名(除了后缀不同),不支持多文件编译,也不支持个性化编译选项。

7. 伪目标

Makefile 中把那些没有任何依赖只有执行动作的目标称为“伪目标”(phony targets)。由于有隐式规则的存在,因此伪目标在某些极端情况下可能会被误编译,比如上述例子中,all 是伪目标,不是真正要编译生成的目标,但如果源码目录中恰巧有一个文件叫 all.c ,那么根据 Makefile 的隐式规则,将会触发 all.c 的编译动作。
如何规避隐式规则这种误操作呢?很简单,明确告诉 Makefile ,all是伪目标,不要编译他:

all:a b

.PHONY:all

上述语句中,.PHONY 是 Makefile 的一个关键字,用来声明伪目标,防止隐式规则滥用。
在 Makefile 中,常见的伪目标除了all之外,还有clean、distclean等,用来清除生成的中间文件,例如:

all:a b

clean:  # 清除所有目标文件、可重定位文件
    rm a b *.o

distclean:clean  # 先执行clean,然后清除所有交换文件、核心转储文件
    rm .*.sw? core

.PHONY:a
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值