Makefile 简介

                     

贡献者: addis

预备知识 Linux 基础

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

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

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

1. 简单的例子

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

代码 1:Makefile
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

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

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

text in a {
file a
}

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

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 中的内容变为

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

2. 伪目标

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

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

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 如下

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 也不会警告。

                     

© 小时科技 保留一切权利