004_Makefile编写

1、make概述

Make 工具是 20 世纪 70 年代发明的用于编程项目编译的辅助工具。make 的编译思路很简单,如果源程序发生了改变,并需要重新构建程序或者其它输出文件时,make 先查看时间戳哪些改变了,并按照要求重新构建这些文件,而不浪费时间重新构建其它文件。

2、make基本规则

一个简单的Makefile规则如下:

TARGET... : PREREQUISITES...

(TAB) COMMAND

               ...

               ...

TARGET:规则的目标,可以是任何类型文件的名称,也可以是一个make执行动作的名称。是你要生成的或操作的命令的索引。

PREREQUISITES:规则的依赖文件,即生成规则目标所需文件的文件名列表。依赖文件的任一处改动,将导致已存在的目标文件过期,需要重新编译。

COMMAND:规则的命令行,可以是任意的shell命令或者是可在shell下执行的程序。一个规则可以有多个命令行,每一条命令占一行,每一个命令行必须以[Tab]字符开始,[Tab]字符告诉make此行是一个命令行。

3、Makefile简单示例

这里的例子由 3 个头文件和 3 C 文件组成,以下是一个最基本也最简单的Makefile文件:

target: main.o fun1.o fun2.o
    gcc -o target main.o fun1.o fun2.o
main.o: main.c def.h
    gcc -c main.c
fun1.o: fun1.c fun1.h
    gcc -c fun1.c
fun2.o:fun2.c fun2.h
    gcc -c fun2.c
clean:
    rm target main.o fun1.o fun2.o

初学者看到这个Makefile 文件之后,也许会想着使用更为简单的写法,于是出现了如下代码:

target: main.o fun1.o fun2.o
    gcc -o target main.c fun1.c fun2.c
clean:
    rm target main.o fun1.o fun2.o

这个 Makefile 看起来似乎更简洁,并且同样能得到正确的可执行文件,但在使用的过程中就会暴露一个弊端:当你修改了头文件,而编译代码时仍然提示目标文件是最新的,修改过的头文件不会被重新编译。所以,这样的 Makefile 文件显然是不妥当的。

第一个 Makefile 示例虽然没有像第二个示例那样的弊端,可一旦文件多了,这样的 Makefile 文件弊端就凸显出来了,随后我们将对它做进一步的改进。

4、Makefile变量

变量的定义有以下几种方式:

IMMEDIATE = DEFERRED

IMMEDIATE ?= DEFERRED

IMMEDIATE := IMMEDIATE

IMMEDIATE += DEFERRED or IMMEDIATE

=” : 引用变量时原样展开,有点类似 C 语言里面的 define

?=”: 条件赋值,在之前没有赋值的情况下才给变量赋值;

:=”: 立即赋值,在变量定义时就直接展开;

+=”: 追加变量。

引用变量时使用$( variable)即可。

同样是上面的例子,我们可以在 Makefile 中使用变量,修改后的 Makefile 文件如下:

OBJS = main.o fun1.o fun2.o
target: $(OBJS)
    gcc -o target $(OBJS)
main.o: main.c def.h
    gcc -c main.c
fun1.o: fun1.c fun1.h
    gcc -c fun1.c
fun2.o:fun2.c fun2.h
    gcc -c fun2.c
clean:
    rm target $(OBJS)

这个 Makefile 似乎得到一点小改进,当添加或减少文件时,只需修改 OBJS 变量,再修改添加或删除一条目标文件的创建规则即可。

5、自动推导规则

Makefile 中我们只需要给出需要重建的目标文件名(一个.o 文件),make 会自动为这个.o 文件寻找合适的依赖文件(对应的.c 文件。对应是指:文件名除后缀外,其余都相同的两个文件),而且使用正确的命令来重建这个目标文件。

这样,在书写 Makefile 时,我们就可以省略掉描述.c 文件和.o 依赖关系的规则,而只 需要给出那些特定的规则描述(.o 目标所需要的.h 文件)。因此上边的例子就可以以更加简 单的方式书写我们的 Makefile

OBJS = main.o fun1.o fun2.o
target: $(OBJS)
    gcc -o target $(OBJS)
main.o: def.h
fun1.o: fun1.h
fun2.o: fun2.h
clean:
    rm target $(OBJS)

6、Makefile的自动化变量

假如你需要书写一个将.c 文件编译到.o 文件的模式规则,那么你该如何为 gcc 书写正确的源文件名?当然了,不能使用任何具体的文件名,因为在每一次执行模式规则时源文件名都是不一样的。为了解决这个问题,就需要使用“自动化变量”,自动化变量的取值是根据具体所执行的规则来决定的,取决于所执行规则的目标和依赖文件名。所谓自动化变量,就是这种变量会把模式中所定义的一系列的文件自动地挨个取出,直至所有的符合模式的文件都取完了。这种自动化变量只应出现在规则的命令中。

下面是所有的自动化变量及其说明:

$@ :表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。

$%:仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%""bar.o""$@"就是"foo.a"。如果目标不是函数库文件(Unix 下是[.a]Windows 下是[.lib]),那么,其值为空。

$<:依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。

$?:所有比目标新的依赖目标的集合。以空格分隔。

7、自动生成依赖关系

模式规则类似于普通规则。只是在模式规则中,目标名中需要包含有模式字符“%” (一个),包含有模式字符“%”的目标被用来匹配一个文件名,“%”可以匹配任何非空字符串。规则的依赖文件中同样可以使用“%”,依赖文件中模式字符“%”的取值情况由目标中的“%”来决定。例如:对于模式规则“%.o : %.c”,它表示的含义是:所有的.o 文件依赖于对应的.c 文件。有一点需要注意的是,"%"的展开发生在变量和函数的展开之后,变量和函数的展开发生在 make 载入 Makefile 时,而模式规则中的"%"则发生在运行时。

8、模式规范示例

下面这个例子表示了把所有的[.c]文件都编译成[.o]文件:

%.o : %.c

$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

其中,自动化变量"$@"表示所有的目标的挨个值,"$<"表示了所有依赖目标的挨个值。

9、通用模板

Makefile生成自动依赖的方法有两种:

1) 将所有.o 文件的依赖关系写入单独文件,然后在 Makefile 中调用该文件

2) 每个.c 文件对应一个.d 文件,将依赖关系写入.d 文件中

第一种方式是比较早的一种方法,现在通常使用第二种方法,这里就第二种方法进行讨论。

使用 gcc -MM 参数可以生成目标文件与源文件的依赖关系,以下是一个通用模块,该模板可以为每个 C 文件建立一个.d 的依赖关系文件:

%.d: %.c
    $(CC) -MM $(INCLUDE) $(CFLAGS) $< > $@.$$$$;\
    sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@;\
    rm -f $@.$$$$
sinclude $(CSRC:.c=.d)

上面的 sinclude 语句是将所有.d 的文件包含进来原样展开。 到这里,我们又可以改进 Makefile

OBJS = main.o fun1.o fun2.o
target: $(OBJS)
    gcc -o target $(OBJS)
#所有的.d 文件依赖于同名的.c 文件
%.d: %.c
    #生成*.o.xxxx 的临时文件
    gcc –MM $< >$@.$$$$;\
    #使用 sed 处理已产生的临时文件并生成此规则的目标文件
    sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@;\
#删除第一行代码创建的临时文件$@.
    rm –f $@.$$$$
#自动产生的依赖关系保存在另外一个文件中,主 Makefile 使用指示符“include”包含这些文件
sinclude $(OBJS:.c=.d)
clean:
    rm target $(OBJS)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Da Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值