图

Make 简介

预备知识 Linux 基础

   Make 通常被用于自动编译程序. 当程序由许多文件构成或使用许多其他的程序库时, 编译的命令往往会变得非常复杂, 如果每次编译都手动输入这些命令会非常麻烦且容易出错. 如果每次输入的编译命令都相同, 一个简单的解决方法是直接把这些命令写进一个 shell script, 每次编译调用即可.

   但这样做会遇到另一个问题, 当程序文件(如 .cpp 文件)较多时, 如果在上次编译以后只修改了某些文件, 我们会希望只将这些文件重新编译(生成对应的 .o 文件)并再次 link, 这样可以大大节约编译时间.

   Linux 系统中, make 就是为了解决这个问题产生的. make 并不是为某个编程语言而设计的, 也不会去分析任何文件的内容. 用户需要写一个名为 Makefile 的文件告诉 make 该怎么做. Makefile 中主要描述了两件事情, 第一是每个文件需要依赖哪些文件才能开始生成, 第二就是每个文件需要用哪些命令生成. 下面举例说明, 为了方便, 这个例子只使用 Linux 常用命令而不涉及任何编程语言.

简单的例子

   我们先在某个目录创建一个简单的 Makefile 文件, 内容如下

1
2
3
4
5
6
7
a :
	echo "file a" > a

b : a
	echo "text in a {" > b
	cat a >> b
	echo "}" >> b
这个 Makefile 指定了两个要生成的文本文件 ab. 先来看含有冒号的两行, 冒号表示依赖关系, 左边是需要生成的文件名, 右边是依赖的文件名. 可见 a 没有任何依赖, 可以直接生成, 而文件 b 依赖于 a, 需要等 a 生成后才能开始生成.

   在每个含有冒号的行后, 可以添加若干行命令, 当 make 决定生成某个文件时, 就执行这些命令. 注意每个命令前必须有一个制表符(用键盘的 tab 键插入). 所以生成 a 文件的命令就是 echo "file a" > a, 即新建文件 a, 内容为 file a. 同理, 生成 b 的命令就是新建文件, 内容为 text in a {...} 其中 ...a 中的文本. 如果 a 文件不存在, 第 6 行命令就无法执行, 所以必须指定 ab 的依赖.

   每个含有冒号的行以及接下来的所有命令构成一个 规则(rule). 冒号左边的文件叫做 目标(target), 右边的文件叫做 依赖(dependency). 如果有多个依赖文件, 用空格隔开即可.

   现在可以在命令行先 cdMakefile 所在的目录, 然后执行 make 命令. 如果一切正常, 程序将会在命令行依次输出执行的命令(Makefile 中第 2 行)以生成 a

1
echo "file a" > a
生成文件 a 的内容为 file a

   但为什么不生成 b 呢? 因为 make 默认情况下只生成第一个出现的目标. 如果想指定生成 b, 执行 make b 即可. 执行过程中命令行显示 Makefile 的 5-7 行被执行了. 但注意第 2 行没有被执行. 这是因为 a 文件已经存在了且上一次 make 后没有被修改过, 所以就不需要再重复生成一次了. b 中的内容如下

1
2
3
text in a {
file a
}

   现在两个文件都生成了, 且没有被修改, 所以若再次执行 make b, 则会显示

1
make: 'b' is up to date.
执行 makemake a 结果类似.

   接下来我们把 a, b 两个文件都删除, 执行一次 make b, 会发现第 2 行和 5-7 行都被执行了, 因为当 make 要生成 b 时发现它的依赖文件 a 并不存在, 所以就会先生成 a. 两文件中内容与之前相同.

   我们来手动把 a 中的内容修改为 modified file a, 再次运行 make b, 发现 Makefile 的 5-7 行被执行, 且 b 中的内容变为

1
2
3
text in a {
modified file a
}
这是因为, make 在生成 b 前, 不但要检查它的所有依赖都存在还要检查他们在上次 make 后是否有更新. 如果任何依赖有更新, b 将会被重新生成.

伪目标

   注意 rule 的目标除了是具体的文件, 也可以是伪目标(phony tarket), 伪目标可以有任意名字. 伪目标的 rule 中可以有命令也可以没有.

   一个特殊的伪目标是 goal. 在上面的 Makefile 中如果我们想让 make 默认生成 b, 可以在文件开始插入 goal 作为第一个目标

1
2
3
4
5
6
7
8
9
goal : b

a :
	echo "file a" > a

b : a
	echo "text in a {" > b
	cat a >> b
	echo "}" >> b
现在执行 make 或者 make goal 就相当于 make b 了.

   另一个常用的伪目标是 clean, 通常用于清除所有被生成的文件. 修改 Makefile 如下

1
2
3
4
5
6
7
8
9
10
11
12
goal : b

a :
	echo "file a" > a

b : a
	echo "text in a {" > b
	cat a >> b
	echo "}" >> b
    
clean :
	rm -f a b
现在执行 make clean 就可以清除两个生成的文件. 使用 -f 选项的好处是, 即使某些被删除的文件不存在, rm 也不会警告.

致读者: 小时物理百科一直以来坚持所有内容免费且不做广告,这导致我们处于日渐严重的亏损状态。长此以往很可能会最终导致我们不得不选择商业化,例如大量广告,内容付费,会员制,甚至被收购。因此,我们鼓起勇气在此请求广大读者热心捐款,使网站得以健康发展。如果看到这条信息的每位读者能慷慨捐助 10 元,我们几天内就能脱离亏损状态,并保证网站能在接下来的一整年里向所有读者继续免费提供优质内容。感谢您的支持。

编辑词条(需要权限) 返回目录 返回主页 捐助项目 © 小时物理百科 保留一切权利