======MakeFile======
//Notes & usage//
----
====Installation====
* 只能在 linux 环境下运行
* Windows 可以使用 [[https://www.mingw-w64.org/getting-started/msys2/|msYS2]]
# ubuntu
sudo apt install build-essential
# check current version
make -v
====Basics====
''Makefile'' 由若干规则(//Rule//)组成。每条规则包含:
* 目标文件(//Target//)
* 依赖文件(//Prerequisites//)
* 生成目标文件的命令
格式为:
Target File: Prerequisites1, Prerequisites2....
# 生成命令以 Tab 开头
complie commands...
比如我们要基于 ''a.txt'' 和 ''b.txt'' 生成文件 ''m.txt'':
m.txt: a.txt, b.txt
cat a.txt, b.txt > m.txt
===多层级 makefile===
假设 ''m.txt'' 是中间件,用于上一级的 ''x.txt'' 生成,那么 makefile 可以写成:
# makefile 会默认执行第一条规则
# 查找依赖的时候会递归向下找寻对应规则
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
可以看到此时 ''m.txt'' 并未生成。这种情况下,''make'' 会向下查找是否有可以支持 ''m.txt'' 生成的规则。
==增量编译==
make 生成的文件会基于依赖文件的最后修改时间来决定是否重新生成。如果当前生成文件使用的是最新的依赖文件,那么 make 会自动跳过生成,并提供对应输入:
# 增量编译
$ make
make: 'x.txt' is up to date.
===伪目标===
从上面的例子中可以看到,make 只有在两种情况下才有新文件的生成动作:
* 目标文件不存在
* 目标文件**过期**
假设我们希望的不是生成文件,而是批量执行一系列命令,那么上述要求就会导致问题。比如我们想批量删除之前例子中的生成文件。一般思路是写一个文件 ''clean'',并把命令都写进去:
clean:
rm-f m.txt
rm-f x.txt
当我们运行的时候,由于 ''clean'' 作为目标,本身没有任何依赖文件,因此 makefile 会认为该目标文件是 up to date 的状态;所以不会有任何命令的执行。\\ \\
这种情况的解决方式是,不要以任何形式的文件作为命令的载体,而是以 ''.PHONY'' 关键词开头,将 ''clean'' 对应的命令全写到 makefile 里面去。''.PHONY'' 关键字会让 makefile 将目标 ''clean'' 视为**伪目标**(//PHONY Target//)。这种情况下的 ''clean'' 会被视作一个动作;不管有没有这个名字的文件,都会执行这个**动作**:
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
# define clean "action"
.PHONY: clean
clean:
rm -f m.txt
rm -f x.txt
运行时,需要单独的使用该目标:
make clean
这样的伪目标还有很多,比如:
make build # 编译程序
make run # 运行程序
make clean # 删除中间产物
make # 默认执行 all → build