1.0 Makefile 基础
在 Linux 下,通过 make
命令即可实现通过 Makefile
文件定义的规则自动化执行任意命令(包括编译)。
即通过一个命令一个文件即可实现预定义好的任意操作。
1.1 规则
Makefile
由若干条规则(Rule)构成,每条规则指出一个目标文件(Target),若干依赖文件(Prerequisites),以及生成目标文件的命令。
例如,如果需要生成文件 m.txt
其由 a.txt
和 b.txt
合并而来,则有如下规则:
# 目标文件:依赖文件 1 依赖文件 2
m.txt: a.txt b.txt
cat a.txt b.txt > m.txt
第一行定义了一条规则,第二行通过tab
规定了一条命令,使用 cat
合并两个文件,其中 #
开头的为注释 make
命令会忽略。
在 makefile 中命令必须使用 tab
定义,空格定义的无效。
由于 make
执行时默认执行第一条规则,所以仿照上述的方法可以写出第二个规则:
x.txt: m.txt c.txt
cat m.txt c.txt > x.txt
m.txt: a.txt b.txt
cat a.txt b.txt > m.txt
此时在系统内某一文件夹创建 a.txt
、b.txt
、c.txt
,输入一些内容执行 make
:
make
默认执行第一条规则,在创建 x.txt
的时候发现 m.txt
不存在,因此先执行规则 m.txt
创建 m.txt
文件,再执行规则 x.txt
。
综上可见,Makefile
定义了一系列的规则,在每个规则满足目标的前提下执行命令,就能创建出一个目标文件。
在编写 Makefile
的时候把默认执行的规则放在第一条,其它的规则顺序无关紧要,因为 make
在执行的时候会自行判断依赖。
此外 make
会打印出每一条执行的命令,便于观察执行顺序。
在上述 make
命令执行完毕后再次执行 make
后,输出如下:
$ make
make: “x.txt”已是最新。
make
检测到 x.txt
为最新的版本,无需再次执行,因为 x.txt
的创建时间晚于它依赖文件的最后修改时间。
可见,make
使用文件的创建和修改时间来判断是否该更新一个目标文件。
如果修改任意依赖文件后,再次执行 make
命令即可触发 x.txt
的更新。
假设修改了 c.txt
文件,则执行 make
命令后会触发 x.txt
的更新,并不会触发 m.txt
的更新,因为 m.txt
的依赖文件并没有变化,所以,make
会根据 Makefile
执行必要的规则,并非无脑执行所有规则。
在编译大型程序时,完全编译一次往往需要几十分钟甚至数小时,若每次对文件修改后都要执行完全编译则太过于浪费时间,Makefile
的存在即可大幅节省编译时间。
当然,对于 Makefile
是否能高效的完成增量更新,取决于规则书写的是否正确,make
本身并不会检查规则逻辑是否正确。
1.2 伪目标
在上述的例子中,m.txt
和 x.txt
是自动生成的文件,所以可以安全地删除。
但是如果我们希望自动删除,则可以编写一个 clean
规则来删除它们:
clean:
rm -f m.txt
rm -f x.txt
clear
命令,与上面所用的命令不同,其没有依赖文件,因此需要执行 clean
命令时必须在命令行使用命令 make clean
值得注意的是,如果在执行 clean
的时候,目录下并没有 clean
文件,每次执行 make clean
命令时都会执行 Makefile
中这个规则的命令。
所以,如果目录下有一个名为 clean
的文件,则此时 Makefile
中的 clean
规则就不会执行了!
如果目录下既需要 clean
文件,还想正常使用 Makefile
文件的 clean
规则,则可以添加一个标识:
.PHONY: clean
clean:
rm -f m.txt
rm -f x.txt
此时的 clean
就不会被视为一个文件,而是伪目标(Phony Target)
在大型项目中约定俗成下将:clean
、install
这些视为伪目标名称,方便用户快速执行对应的任务。
一般不会有人在目录下创建名为 clean
的文件,除非有人蓄意搞破坏。
1.3 执行多条命令
1.4 控制打印
1.5 控制错误
1.6 小结
编写 Makefile
就是编写一系列的规则,用来告诉 make
该如何执行这些规则,从而生成我们最终需要的文件。
评论区(暂无评论)